<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][7952] trunk: Automatically check for newly created items when viewing the Activity Stream.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://buddypress.trac.wordpress.org/changeset/7952">7952</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2014-02-21 14:49:33 +0000 (Fri, 21 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Automatically check for newly created items when viewing the Activity Stream.

Leveraging WP's Heartbeat API, this new functionality performs periodic checks
to see whether new activity items matching the current filter have been posted
since the page was originally loaded. If new items are found, a Load Newest
link is inserted into the top of the stream, which will insert the new items.

A filter is included, bp_activity_heartbeat_pulse, that allows site owners to
set a specific interval for these checks. The default value is 15 seconds, or
whatever your global Heartbeat interval is.

To disable the feature, there is a new setting "Automatically check for new
activity items when viewing the activity stream".

Fixes <a href="http://buddypress.trac.wordpress.org/ticket/5328">#5328</a>

Props imath, boonebgorges</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpactivitybpactivityfiltersphp">trunk/bp-activity/bp-activity-filters.php</a></li>
<li><a href="#trunkbpactivitybpactivityfunctionsphp">trunk/bp-activity/bp-activity-functions.php</a></li>
<li><a href="#trunkbpactivitybpactivitytemplatephp">trunk/bp-activity/bp-activity-template.php</a></li>
<li><a href="#trunkbpcoreadminbpcoresettingsphp">trunk/bp-core/admin/bp-core-settings.php</a></li>
<li><a href="#trunkbpcorebpcoreadminphp">trunk/bp-core/bp-core-admin.php</a></li>
<li><a href="#trunkbpcorebpcorefunctionsphp">trunk/bp-core/bp-core-functions.php</a></li>
<li><a href="#trunkbpcorebpcoreoptionsphp">trunk/bp-core/bp-core-options.php</a></li>
<li><a href="#trunkbptemplatesbplegacybuddypressfunctionsphp">trunk/bp-templates/bp-legacy/buddypress-functions.php</a></li>
<li><a href="#trunkbptemplatesbplegacycssbuddypresscss">trunk/bp-templates/bp-legacy/css/buddypress.css</a></li>
<li><a href="#trunkbptemplatesbplegacyjsbuddypressjs">trunk/bp-templates/bp-legacy/js/buddypress.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpactivitybpactivityfiltersphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-filters.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-filters.php        2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-activity/bp-activity-filters.php   2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -396,3 +396,152 @@
</span><span class="cx"> 
</span><span class="cx">  return apply_filters( 'bp_activity_truncate_entry', $excerpt, $text, $append_text );
</span><span class="cx"> }
</span><ins>+
+/**
+ * Include extra javascript dependencies for activity component.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @uses bp_activity_do_heartbeat() to check if heartbeat is required.
+ *
+ * @param array $js_handles The original dependencies.
+ * @return array $js_handles The new dependencies.
+ */
+function bp_activity_get_js_dependencies( $js_handles = array() ) {
+       if ( bp_activity_do_heartbeat() ) {
+               $js_handles[] = 'heartbeat';
+       }
+
+       return $js_handles;
+}
+add_filter( 'bp_core_get_js_dependencies', 'bp_activity_get_js_dependencies', 10, 1 );
+
+/**
+ * Add a just-posted classes to the most recent activity item.
+ *
+ * We use these classes to avoid pagination issues when items are loaded
+ * dynamically into the activity stream.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param string $classes
+ * @return string $classes
+ */
+function bp_activity_newest_class( $classes = '' ) {
+       $bp = buddypress();
+
+       if ( ! empty( $bp->activity->new_update_id ) && $bp->activity->new_update_id == bp_get_activity_id() ) {
+               $classes .= ' new-update';
+       }
+
+       $classes .= ' just-posted';
+       return $classes;
+}
+
+/**
+ * Use WordPress Heartbeat API to check for latest activity update.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @uses bp_activity_get_last_updated() to get the recorded date of the last activity
+
+ * @param array $response
+ * @param array $data
+ * @return array $response
+ */
+function bp_activity_heartbeat_last_recorded( $response = array(), $data = array() ) {
+       $bp = buddypress();
+
+       if ( empty( $data['bp_activity_last_id'] ) ) {
+               return $response;
+       }
+
+       // Use the querystring argument stored in the cookie (to preserve
+       // filters), but force the offset to get only new items
+       $activity_latest_args = bp_parse_args(
+               bp_ajax_querystring( 'activity' ),
+               array( 'offset' => absint( $data['bp_activity_last_id'] ) + 1 ),
+               'activity_latest_args'
+       );
+
+       $newest_activities = array();
+       $last_activity_id  = 0;
+
+       // Temporarly add a just-posted class for new activity items
+       add_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
+
+       ob_start();
+       if ( bp_has_activities( $activity_latest_args ) ) {
+               while ( bp_activities() ) {
+                       bp_the_activity();
+
+                       if ( $last_activity_id < bp_get_activity_id() ) {
+                               $last_activity_id = bp_get_activity_id();
+                       }
+
+                       bp_get_template_part( 'activity/entry' );
+               }
+       }
+
+       $newest_activities['activities'] = ob_get_contents();
+       $newest_activities['last_id']    = $last_activity_id;
+       ob_end_clean();
+
+       // Remove the temporary filter
+       remove_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
+
+       if ( ! empty( $newest_activities['last_id'] ) ) {
+               $response['bp_activity_newest_activities'] = $newest_activities;
+       }
+
+       return $response;
+}
+add_filter( 'heartbeat_received', 'bp_activity_heartbeat_last_recorded', 10, 2 );
+add_filter( 'heartbeat_nopriv_received', 'bp_activity_heartbeat_last_recorded', 10, 2 );
+
+/**
+ * Set the strings for WP HeartBeat API where needed.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param array $strings Localized strings.
+ * @return array $strings
+ */
+function bp_activity_heartbeat_strings( $strings = array() ) {
+
+       if ( ! bp_activity_do_heartbeat() ) {
+               return $strings;
+       }
+
+       $global_pulse = 0;
+
+       // Check whether the global heartbeat settings already exist.
+       $heartbeat_settings = apply_filters( 'heartbeat_settings', array() );
+       if ( ! empty( $heartbeat_settings['interval'] ) ) {
+               // 'Fast' is 5
+               $global_pulse = is_numeric( $heartbeat_settings['interval'] ) ? absint( $heartbeat_settings['interval'] ) : 5;
+       }
+
+       // Filter here to specify a BP-specific pulse frequency
+       $bp_activity_pulse = apply_filters( 'bp_activity_heartbeat_pulse', 15 );
+
+       /**
+        * Use the global pulse value unless:
+        * a. the BP-specific value has been specifically filtered, or
+        * b. it doesn't exist (ie, BP will be the only one using the heartbeat,
+        *    so we're responsible for enabling it)
+        */
+       if ( has_filter( 'bp_activity_heartbeat_pulse' ) || empty( $global_pulse ) ) {
+               $pulse = $bp_activity_pulse;
+       } else {
+               $pulse = $global_pulse;
+       }
+
+       $strings = array_merge( $strings, array(
+               'newest' => __( 'Load Newest', 'buddypress' ),
+               'pulse'  => absint( $pulse ),
+       ) );
+
+       return $strings;
+}
+add_filter( 'bp_core_get_js_strings', 'bp_activity_heartbeat_strings', 10, 1 );
</ins></span></pre></div>
<a id="trunkbpactivitybpactivityfunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-functions.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-functions.php      2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-activity/bp-activity-functions.php 2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -1849,3 +1849,42 @@
</span><span class="cx"> function bp_embed_activity_save_cache( $cache, $cachekey, $id ) {
</span><span class="cx">  bp_activity_update_meta( $id, $cachekey, $cache );
</span><span class="cx"> }
</span><ins>+
+/**
+ * Should we use Heartbeat to refresh activities?
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @uses bp_is_activity_heartbeat_active() to check if heatbeat setting is on.
+ * @uses bp_is_activity_directory() to check if the current page is the activity
+ *       directory.
+ * @uses bp_is_active() to check if the group component is active.
+ * @uses bp_is_group_activity() to check if on a single group, the current page
+ *       is the group activities.
+ * @uses bp_is_group_home() to check if the current page is a single group home
+ *       page.
+ *
+ * @return bool True if activity heartbeat is enabled, otherwise false.
+ */
+function bp_activity_do_heartbeat() {
+       $retval = false;
+
+       if ( ! bp_is_activity_heartbeat_active() ) {
+               return $retval;
+       }
+
+       if ( bp_is_activity_directory() ) {
+               $retval = true;
+       }
+
+       if ( bp_is_active( 'groups') ) {
+               // If no custom front, then activities are loaded in group's home
+               $has_custom_front = bp_locate_template( array( 'groups/single/front.php' ), false, true );
+
+               if ( bp_is_group_activity() || ( ! $has_custom_front && bp_is_group_home() ) ) {
+                       $retval = true;
+               }
+       }
+
+       return $retval;
+}
</ins></span></pre></div>
<a id="trunkbpactivitybpactivitytemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-template.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-template.php       2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-activity/bp-activity-template.php  2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -776,7 +776,12 @@
</span><span class="cx"> function bp_activity_has_more_items() {
</span><span class="cx">  global $activities_template;
</span><span class="cx"> 
</span><del>-       $remaining_pages = floor( ( $activities_template->total_activity_count - 1 ) / ( $activities_template->pag_num * $activities_template->pag_page ) );
</del><ins>+        $remaining_pages = 0;
+
+       if ( ! empty( $activities_template->pag_page ) ) {
+               $remaining_pages = floor( ( $activities_template->total_activity_count - 1 ) / ( $activities_template->pag_num * $activities_template->pag_page ) );
+       }
+
</ins><span class="cx">   $has_more_items  = (int) $remaining_pages ? true : false;
</span><span class="cx"> 
</span><span class="cx">  return apply_filters( 'bp_activity_has_more_items', $has_more_items );
</span></span></pre></div>
<a id="trunkbpcoreadminbpcoresettingsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/admin/bp-core-settings.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/admin/bp-core-settings.php 2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-core/admin/bp-core-settings.php    2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -104,6 +104,20 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Allow Heartbeat to refresh activity stream.
+ *
+ * @since BuddyPress (2.0.0)
+ */
+function bp_admin_setting_callback_heartbeat() {
+?>
+
+       <input id="_bp_enable_heartbeat_refresh" name="_bp_enable_heartbeat_refresh" type="checkbox" value="1" <?php checked( bp_is_activity_heartbeat_active( true ) ); ?> />
+       <label for="_bp_enable_heartbeat_refresh"><?php _e( 'Automatically check for new items while viewing the activity stream', 'buddypress' ); ?></label>
+
+<?php
+}
+
+/**
</ins><span class="cx">  * Sanitization for _bp_force_buddyvar
</span><span class="cx">  *
</span><span class="cx">  * If upgraded to 1.6 and you chose to keep the BuddyBar, a checkbox asks if you want to switch to
</span></span></pre></div>
<a id="trunkbpcorebpcoreadminphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/bp-core-admin.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/bp-core-admin.php  2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-core/bp-core-admin.php     2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -330,6 +330,10 @@
</span><span class="cx">                  add_settings_field( 'bp-disable-blogforum-comments', __( 'Blog &amp; Forum Comments', 'buddypress' ), 'bp_admin_setting_callback_blogforum_comments', 'buddypress', 'bp_activity' );
</span><span class="cx">                  register_setting( 'buddypress', 'bp-disable-blogforum-comments', 'bp_admin_sanitize_callback_blogforum_comments' );
</span><span class="cx"> 
</span><ins>+                       // Activity Heartbeat refresh
+                       add_settings_field( '_bp_enable_heartbeat_refresh', __( 'Activity auto-refresh', 'buddypress' ), 'bp_admin_setting_callback_heartbeat', 'buddypress', 'bp_activity' );
+                       register_setting( 'buddypress', '_bp_enable_heartbeat_refresh', 'intval' );
+
</ins><span class="cx">                   // Allow activity akismet
</span><span class="cx">                  if ( is_plugin_active( 'akismet/akismet.php' ) && defined( 'AKISMET_VERSION' ) ) {
</span><span class="cx">                          add_settings_field( '_bp_enable_akismet', __( 'Akismet',          'buddypress' ), 'bp_admin_setting_callback_activity_akismet', 'buddypress', 'bp_activity' );
</span></span></pre></div>
<a id="trunkbpcorebpcorefunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/bp-core-functions.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/bp-core-functions.php      2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-core/bp-core-functions.php 2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -1852,3 +1852,16 @@
</span><span class="cx"> 
</span><span class="cx">  return $nav_item_url;
</span><span class="cx"> }
</span><ins>+
+/**
+ * Get the javascript dependencies for buddypress.js.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @uses apply_filters() to allow other component to load extra dependencies
+ *
+ * @return array The javascript dependencies.
+ */
+function bp_core_get_js_dependencies() {
+       return apply_filters( 'bp_core_get_js_dependencies', array( 'jquery' ) );
+}
</ins></span></pre></div>
<a id="trunkbpcorebpcoreoptionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/bp-core-options.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/bp-core-options.php        2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-core/bp-core-options.php   2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -76,6 +76,11 @@
</span><span class="cx">          // Users from all sites can post
</span><span class="cx">          '_bp_enable_akismet'              => true,
</span><span class="cx"> 
</span><ins>+               /** Activity HeartBeat ************************************************/
+
+               // HeartBeat is on to refresh activities
+               '_bp_enable_heartbeat_refresh'    => true,
+
</ins><span class="cx">           /** BuddyBar **********************************************************/
</span><span class="cx"> 
</span><span class="cx">          // Force the BuddyBar
</span><span class="lines">@@ -586,6 +591,21 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Check whether Activity Heartbeat refresh is enabled.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @uses bp_get_option() To get the Heartbeat option.
+ *
+ * @param bool $default Optional. Fallback value if not found in the database.
+ *        Default: true.
+ * @return bool True if Heartbeat refresh is enabled, otherwise false.
+ */
+function bp_is_activity_heartbeat_active( $default = true ) {
+       return (bool) apply_filters( 'bp_is_activity_heartbeat_active', (bool) bp_get_option( '_bp_enable_heartbeat_refresh', $default ) );
+}
+
+/**
</ins><span class="cx">  * Get the current theme package ID.
</span><span class="cx">  *
</span><span class="cx">  * @since BuddyPress (1.7.0)
</span></span></pre></div>
<a id="trunkbptemplatesbplegacybuddypressfunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-templates/bp-legacy/buddypress-functions.php (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-templates/bp-legacy/buddypress-functions.php    2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-templates/bp-legacy/buddypress-functions.php       2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -225,12 +225,12 @@
</span><span class="cx">          // Enqueue the global JS, if found - AJAX will not work
</span><span class="cx">          // without it
</span><span class="cx">          if ( isset( $asset['location'], $asset['handle'] ) ) {
</span><del>-                       wp_enqueue_script( $asset['handle'], $asset['location'], array( 'jquery' ), $this->version );
</del><ins>+                        wp_enqueue_script( $asset['handle'], $asset['location'], bp_core_get_js_dependencies(), $this->version );
</ins><span class="cx">           }
</span><span class="cx"> 
</span><span class="cx">          // Add words that we need to use in JS to the end of the page
</span><span class="cx">          // so they can be translated and still used.
</span><del>-               $params = array(
</del><ins>+                $params = apply_filters( 'bp_core_get_js_strings', array(
</ins><span class="cx">                   'accepted'            => __( 'Accepted', 'buddypress' ),
</span><span class="cx">                  'close'               => __( 'Close', 'buddypress' ),
</span><span class="cx">                  'comments'            => __( 'comments', 'buddypress' ),
</span><span class="lines">@@ -244,7 +244,7 @@
</span><span class="cx">                  'show_x_comments'     => __( 'Show all %d comments', 'buddypress' ),
</span><span class="cx">                  'unsaved_changes'     => __( 'Your profile has unsaved changes. If you leave the page, the changes will be lost.', 'buddypress' ),
</span><span class="cx">                  'view'                => __( 'View', 'buddypress' ),
</span><del>-               );
</del><ins>+                ) );
</ins><span class="cx">           wp_localize_script( $asset['handle'], 'BP_DTheme', $params );
</span><span class="cx"> 
</span><span class="cx">          // Maybe enqueue comment reply JS
</span><span class="lines">@@ -511,6 +511,11 @@
</span><span class="cx">          $qs[] = 'exclude=' . implode( ',', $just_posted );
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+       // to get newest activities
+       if ( ! empty( $_POST['offset'] ) ) {
+               $qs[] = 'offset=' . intval( $_POST['offset'] );
+       }
+
</ins><span class="cx">   $object_search_text = bp_get_search_default_text( $object );
</span><span class="cx">          if ( ! empty( $_POST['search_terms'] ) && $object_search_text != $_POST['search_terms'] && 'false' != $_POST['search_terms'] && 'undefined' != $_POST['search_terms'] )
</span><span class="cx">          $qs[] = 'search_terms=' . $_POST['search_terms'];
</span><span class="lines">@@ -649,6 +654,8 @@
</span><span class="cx">  * @since BuddyPress (1.2)
</span><span class="cx">  */
</span><span class="cx"> function bp_legacy_theme_post_update() {
</span><ins>+       $bp = buddypress();
+
</ins><span class="cx">   // Bail if not a POST action
</span><span class="cx">  if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) )
</span><span class="cx">          return;
</span><span class="lines">@@ -677,13 +684,25 @@
</span><span class="cx">  if ( empty( $activity_id ) )
</span><span class="cx">          exit( '-1<div id="message" class="error"><p>' . __( 'There was a problem posting your update, please try again.', 'buddypress' ) . '</p></div>' );
</span><span class="cx"> 
</span><del>-       if ( bp_has_activities ( 'include=' . $activity_id ) ) {
</del><ins>+        if ( ! empty( $_POST['offset'] ) && $last_id = absint( $_POST['offset'] ) ) {
+               $activity_args = array( 'offset' => $last_id );
+               $bp->activity->new_update_id = $activity_id;
+               add_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
+       } else {
+               $activity_args = array( 'include' => $activity_id );
+       }
+
+       if ( bp_has_activities ( $activity_args ) ) {
</ins><span class="cx">           while ( bp_activities() ) {
</span><span class="cx">                  bp_the_activity();
</span><span class="cx">                  bp_get_template_part( 'activity/entry' );
</span><span class="cx">          }
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+       if ( ! empty( $last_id ) ) {
+               remove_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
+       }
+
</ins><span class="cx">   exit;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbptemplatesbplegacycssbuddypresscss"></a>
<div class="modfile"><h4>Modified: trunk/bp-templates/bp-legacy/css/buddypress.css (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-templates/bp-legacy/css/buddypress.css  2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-templates/bp-legacy/css/buddypress.css     2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -290,14 +290,16 @@
</span><span class="cx">  margin-left: 1em;
</span><span class="cx">  white-space: nowrap;
</span><span class="cx"> }
</span><del>-#buddypress .activity-list li.load-more {
</del><ins>+#buddypress .activity-list li.load-more,
+#buddypress .activity-list li.load-newest {
</ins><span class="cx">   background: #f0f0f0;
</span><span class="cx">  font-size: 110%;
</span><span class="cx">  margin: 15px 0;
</span><span class="cx">  padding: 10px 15px;
</span><span class="cx">  text-align: center;
</span><span class="cx"> }
</span><del>-#buddypress .activity-list li.load-more a {
</del><ins>+#buddypress .activity-list li.load-more a,
+#buddypress .activity-list li.load-newest a {
</ins><span class="cx">   color: #4D4D4D;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbptemplatesbplegacyjsbuddypressjs"></a>
<div class="modfile"><h4>Modified: trunk/bp-templates/bp-legacy/js/buddypress.js (7951 => 7952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-templates/bp-legacy/js/buddypress.js    2014-02-21 14:49:28 UTC (rev 7951)
+++ trunk/bp-templates/bp-legacy/js/buddypress.js       2014-02-21 14:49:33 UTC (rev 7952)
</span><span class="lines">@@ -4,6 +4,10 @@
</span><span class="cx"> // Global variable to prevent multiple AJAX requests
</span><span class="cx"> var bp_ajax_request = null;
</span><span class="cx"> 
</span><ins>+// Global variables to temporarly store newest activities
+var newest_activities = '';
+var activity_last_id  = 0;
+
</ins><span class="cx"> jq(document).ready( function() {
</span><span class="cx">  /**** Page Load Actions *******************************************************/
</span><span class="cx"> 
</span><span class="lines">@@ -53,6 +57,20 @@
</span><span class="cx">          if ( $whats_new_form.hasClass("submitted") ) {
</span><span class="cx">                  $whats_new_form.removeClass("submitted");     
</span><span class="cx">          }
</span><ins>+
+               // Return to the 'All Members' tab and 'Everything' filter,
+               // to avoid inconsistencies with the heartbeat integration
+               var $activity_all = jq( '#activity-all' );
+               if ( $activity_all.length  ) {
+                       if ( ! $activity_all.hasClass( 'selected' ) ) {
+                               // reset to everyting
+                               jq( '#activity-filter-select select' ).val( '-1' );
+                               $activity_all.children( 'a' ).trigger( "click" );
+                       } else if ( '-1' != jq( '#activity-filter-select select' ).val() ) {
+                               jq( '#activity-filter-select select' ).val( '-1' );
+                               jq( '#activity-filter-select select' ).trigger( 'change' );
+                       }
+               }
</ins><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">  /* On blur, shrink if it's empty */
</span><span class="lines">@@ -73,6 +91,7 @@
</span><span class="cx"> 
</span><span class="cx">  /* New posts */
</span><span class="cx">  jq("#aw-whats-new-submit").on( 'click', function() {
</span><ins>+               var last_displayed_id = 0;
</ins><span class="cx">           var button = jq(this);
</span><span class="cx">          var form = button.closest("form#whats-new-form");
</span><span class="cx"> 
</span><span class="lines">@@ -91,7 +110,14 @@
</span><span class="cx">          var object = '';
</span><span class="cx">          var item_id = jq("#whats-new-post-in").val();
</span><span class="cx">          var content = jq("#whats-new").val();
</span><ins>+               var firstrow = jq( '#buddypress ul.activity-list li' ).first();
</ins><span class="cx"> 
</span><ins>+               if ( firstrow.hasClass( 'load-newest' ) ) {
+                       last_displayed_id = firstrow.next().prop( 'id' ) ? firstrow.next().prop( 'id' ).replace( 'activity-','' ) : 0;
+               } else {
+                       last_displayed_id = firstrow.prop( 'id' ) ? firstrow.prop( 'id' ).replace( 'activity-','' ) : 0;
+               }
+
</ins><span class="cx">           /* Set object for non-profile posts */
</span><span class="cx">          if ( item_id > 0 ) {
</span><span class="cx">                  object = jq("#whats-new-post-object").val();
</span><span class="lines">@@ -104,6 +130,7 @@
</span><span class="cx">                  'content': content,
</span><span class="cx">                  'object': object,
</span><span class="cx">                  'item_id': item_id,
</span><ins>+                       'offset': last_displayed_id,
</ins><span class="cx">                   '_bp_as_nonce': jq('#_bp_as_nonce').val() || ''
</span><span class="cx">          },
</span><span class="cx">          function(response) {
</span><span class="lines">@@ -125,9 +152,14 @@
</span><span class="cx">                                  jq("div.activity").append( '<ul id="activity-stream" class="activity-list item-list">' );
</span><span class="cx">                          }
</span><span class="cx"> 
</span><ins>+                               if ( firstrow.hasClass( 'load-newest' ) )
+                                       firstrow.remove();
+
</ins><span class="cx">                           jq("#activity-stream").prepend(response);
</span><del>-                               jq("#activity-stream li:first").addClass('new-update just-posted');
</del><span class="cx"> 
</span><ins>+                               if ( ! last_displayed_id )
+                                       jq("#activity-stream li:first").addClass('new-update just-posted');
+
</ins><span class="cx">                           if ( 0 != jq("#latest-update").length ) {
</span><span class="cx">                                  var l = jq("#activity-stream li.new-update .activity-content .activity-inner p").html();
</span><span class="cx">                                  var v = jq("#activity-stream li.new-update .activity-content .activity-header p a.view").attr('href');
</span><span class="lines">@@ -149,6 +181,10 @@
</span><span class="cx">                          jq("li.new-update").hide().slideDown( 300 );
</span><span class="cx">                          jq("li.new-update").removeClass( 'new-update' );
</span><span class="cx">                          jq("#whats-new").val('');
</span><ins>+
+                               // reset vars to get newest activities
+                               newest_activities = '';
+                               activity_last_id  = 0;
</ins><span class="cx">                   }
</span><span class="cx"> 
</span><span class="cx">                  jq("#whats-new-options").animate({
</span><span class="lines">@@ -287,6 +323,12 @@
</span><span class="cx">                                  li.children('#message').hide().fadeIn(300);
</span><span class="cx">                          } else {
</span><span class="cx">                                  li.slideUp(300);
</span><ins>+
+                                       // reset vars to get newest activities
+                                       if ( activity_last_id == id ) {
+                                               newest_activities = '';
+                                               activity_last_id  = 0;
+                                       }
</ins><span class="cx">                           }
</span><span class="cx">                  });
</span><span class="cx"> 
</span><span class="lines">@@ -311,6 +353,11 @@
</span><span class="cx">                                  li.children( '#message' ).hide().fadeIn(300);
</span><span class="cx">                          } else {
</span><span class="cx">                                  li.slideUp( 300 );
</span><ins>+                                       // reset vars to get newest activities
+                                       if ( activity_last_id == id ) {
+                                               newest_activities = '';
+                                               activity_last_id  = 0;
+                                       }
</ins><span class="cx">                           }
</span><span class="cx">                  });
</span><span class="cx"> 
</span><span class="lines">@@ -353,6 +400,18 @@
</span><span class="cx"> 
</span><span class="cx">                  return false;
</span><span class="cx">          }
</span><ins>+
+               /* Load newest updates at the top of the list */
+               if ( target.parent().hasClass('load-newest') ) {
+
+                       event.preventDefault();
+
+                       target.parent().hide();
+                       jq( '#buddypress ul.activity-list' ).prepend( newest_activities );
+
+                       // reset the newest activities now they're displayed
+                       newest_activities = '';
+               }
</ins><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">  // Activity "Read More" links
</span><span class="lines">@@ -1378,7 +1437,54 @@
</span><span class="cx">  /* if js is enabled then replace the no-js class by a js one */
</span><span class="cx">  if( jq('body').hasClass('no-js') )
</span><span class="cx">          jq('body').attr('class', jq('body').attr('class').replace( /no-js/,'js' ) );
</span><del>-               
</del><ins>+
+       /** Activity HeartBeat ************************************************/
+
+       // Set the interval and the namespace event
+       if ( typeof wp != 'undefined' && typeof wp.heartbeat != 'undefined' && typeof BP_DTheme.pulse != 'undefined' ) {
+
+               wp.heartbeat.interval( Number( BP_DTheme.pulse ) );
+
+               jq.fn.extend({
+                       'heartbeat-send': function() {
+                       return this.bind( 'heartbeat-send.buddypress' );
+               },
+           });
+
+       }
+
+       // Set the last id to request after
+       jq( document ).on( 'heartbeat-send.buddypress', function( e, data ) {
+
+               // First row is default latest activity id
+               if ( jq( '#buddypress ul.activity-list li' ).first().prop( 'id' ) ) {
+                       firstrow = jq( '#buddypress ul.activity-list li' ).first().prop( 'id' ).replace( 'activity-','' );
+               } else {
+                       firstrow = 0;
+               }
+
+               if ( 0 == activity_last_id || Number( firstrow ) > activity_last_id )
+                       activity_last_id = Number( firstrow );
+
+               data['bp_activity_last_id'] = activity_last_id;
+       });
+
+       // Increment newest_activities and activity_last_id if data has been returned
+       jq( document ).on( 'heartbeat-tick', function( e, data ) {
+
+               // Only proceed if we have newest activities
+               if ( ! data['bp_activity_newest_activities'] ) {
+                       return;
+               }
+
+               newest_activities = data['bp_activity_newest_activities']['activities'] + newest_activities;
+               activity_last_id  = Number( data['bp_activity_newest_activities']['last_id'] );
+
+               if ( jq( '#buddypress ul.activity-list li' ).first().hasClass( 'load-newest' ) )
+                       return;
+
+               jq( '#buddypress ul.activity-list' ).prepend( '<li class="load-newest"><a href="#newest">' + BP_DTheme.newest + '</a></li>' );
+       });
</ins><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> /* Setup activity scope and filter based on the current cookie settings. */
</span></span></pre>
</div>
</div>

</body>
</html>