diff --git a/reviewboard/reviews/datagrids.py b/reviewboard/reviews/datagrids.py
index 55aa62cee13f0d9e0250b7a7412ef1811b641261..9a7caf69b6bd203c58467480fa4c05f0ced18498 100644
--- a/reviewboard/reviews/datagrids.py
+++ b/reviewboard/reviews/datagrids.py
@@ -651,6 +651,8 @@ class DashboardDataGrid(ReviewRequestDataGrid):
             self.queryset = ReviewRequest.objects.to_user(
                 user, user, local_site=self.local_site)
             self.title = _(u"All Incoming Review Requests")
+        elif view == 'action_feed':
+            self.title = _(u"Latest Changes")
         else:
             raise Http404
 
diff --git a/reviewboard/reviews/models.py b/reviewboard/reviews/models.py
index a73cdfbe582617d69743a57b43914d5247d5c4fc..d280991459b5d62e0f8e92f11cbef3228fb3a9d8 100644
--- a/reviewboard/reviews/models.py
+++ b/reviewboard/reviews/models.py
@@ -1,6 +1,7 @@
 import os
 import re
 
+from actstream import action, actions
 from django.contrib.auth.models import User
 from django.db import models
 from django.db.models import Q
@@ -682,6 +683,7 @@ class ReviewRequest(models.Model):
             review_request_closed.send(sender=self.__class__, user=user,
                                        review_request=self,
                                        type=type)
+            action.send(user, target=self, verb="has changed status")
         else:
             # Update submission description.
             changedesc = self.changedescs.filter(public=True).latest()
@@ -691,7 +693,8 @@ class ReviewRequest(models.Model):
 
             # Needed to renew last-update.
             self.save()
-
+            action.send(user, target=self,
+                        verb="changed submission description")
         try:
             draft = self.draft.get()
         except ReviewRequestDraft.DoesNotExist:
@@ -729,6 +732,7 @@ class ReviewRequest(models.Model):
 
         review_request_reopened.send(sender=self.__class__, user=user,
                                      review_request=self)
+        action.send(user, verb="reopened", target=self)
 
     def update_changenum(self,changenum, user=None):
         if (user and not self.is_mutable_by(user)):
@@ -779,12 +783,20 @@ class ReviewRequest(models.Model):
         else:
             changes = None
 
+        if changes is not None:
+            verb = "updated"
+        else:
+            verb = "created"
+        verb += " the review request"
+        action.send(user, verb=verb, target=self)
+
         self.public = True
         self.save(update_counts=True)
 
         review_request_published.send(sender=self.__class__, user=user,
                                       review_request=self,
                                       changedesc=changes)
+        actions.follow(user, self, send_action=False, actor_only=False)
 
     def _update_counts(self):
         from reviewboard.accounts.models import Profile, LocalSiteProfile
@@ -1647,13 +1659,18 @@ class Review(models.Model):
         # Atomicly update the shipit_count
         if self.ship_it:
             self.review_request.increment_shipit_count()
+        verb = "published a "
 
         if self.is_reply():
             reply_published.send(sender=self.__class__,
                                  user=user, reply=self)
+            verb += "reply"
         else:
             review_published.send(sender=self.__class__,
                                   user=user, review=self)
+            verb += "review"
+        action.send(user, target=self.review_request,
+                    verb=verb, action_object=self)
 
     def delete(self):
         """
diff --git a/reviewboard/reviews/templatetags/reviewtags.py b/reviewboard/reviews/templatetags/reviewtags.py
index 78ab5cef77c8364ae7815f0d18666f412293508a..616daa9af00129cdcb102b3d92756d718ea3a42d 100644
--- a/reviewboard/reviews/templatetags/reviewtags.py
+++ b/reviewboard/reviews/templatetags/reviewtags.py
@@ -377,7 +377,9 @@ def dashboard_entry(context, level, text, view, param=None):
     url = None
     group_name = None
 
-    if view == 'to-group':
+    if datagrid is None:
+        show_count = False
+    elif view == 'to-group':
         group_name = param
         count = datagrid.counts['groups'].get(group_name,
             datagrid.counts['starred_groups'].get(group_name, 0))
@@ -392,6 +394,8 @@ def dashboard_entry(context, level, text, view, param=None):
     elif view == "url":
         url = param
         show_count = False
+    elif view == "action_feed":
+        show_count = False
     else:
         raise template.TemplateSyntaxError, \
             "Invalid view type '%s' passed to 'dashboard_entry' tag." % view
diff --git a/reviewboard/reviews/urls.py b/reviewboard/reviews/urls.py
index 351084f1104656a8da29a81f4f7ecacb54aa3090..094c61df5799deda91de1af61f64f35714f44977 100644
--- a/reviewboard/reviews/urls.py
+++ b/reviewboard/reviews/urls.py
@@ -60,4 +60,3 @@ urlpatterns = patterns('reviewboard.reviews.views',
     # Search
     url(r'^search/$', 'search', name="search"),
 )
-
diff --git a/reviewboard/reviews/views.py b/reviewboard/reviews/views.py
index 8a9aecc2844900fc07d895610eac3e08c37b843b..4de4ca3dbf04c1e5f95da2e141fee92943739939 100644
--- a/reviewboard/reviews/views.py
+++ b/reviewboard/reviews/views.py
@@ -1,7 +1,7 @@
 import logging
 import time
-from datetime import datetime
 
+from actstream.models import user_stream
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.sites.models import Site
@@ -588,9 +588,9 @@ def dashboard(request,
         * 'watched-groups'
         * 'incoming'
         * 'mine'
+        * 'action_feed'
     """
     view = request.GET.get('view', None)
-
     if local_site_name:
         local_site = get_object_or_404(LocalSite, name=local_site_name)
         if not local_site.is_accessible_by(request.user):
@@ -602,6 +602,8 @@ def dashboard(request,
         # This is special. We want to return a list of groups, not
         # review requests.
         grid = WatchedGroupDataGrid(request, local_site=local_site)
+    elif view == "action_feed" or view is None:
+        return action_feed(request)
     else:
         grid = DashboardDataGrid(request, local_site=local_site)
 
@@ -610,6 +612,19 @@ def dashboard(request,
     })
 
 
+@login_required
+@valid_prefs_required
+def action_feed(request):
+    stream = user_stream(request.user).exclude(actor_object_id = request.user.pk)
+    context = {
+        'request': request,
+        'user': request.user,
+        'stream': stream
+        }
+    return HttpResponse(
+        render_to_string("reviews/action_feed.html", context))
+
+
 @check_login_required
 def group(request,
           name,
@@ -894,6 +909,7 @@ def diff_fragment(request,
                               interdiffset_id, chunkindex, template_name)
 
 
+
 @check_login_required
 def preview_review_request_email(
     request,
diff --git a/reviewboard/settings.py b/reviewboard/settings.py
index 689c1164feb524add923125d7942f6efd570b494..49c542278d1b0b7680c6199c1a9edd1ace67a8e6 100644
--- a/reviewboard/settings.py
+++ b/reviewboard/settings.py
@@ -123,6 +123,7 @@ STATICFILES_FINDERS = (
 STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
 
 RB_BUILTIN_APPS = [
+    'actstream',
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
@@ -326,3 +327,7 @@ elif DEBUG:
 
 # Packages to unit test
 TEST_PACKAGES = ['reviewboard']
+
+ACTSTREAM_ACTION_MODELS = ['auth.User', 'reviews.reviewrequest',
+                           'reviews.review']
+ACTSTREAM_MANAGER = 'actstream.managers.ActionManager'
diff --git a/reviewboard/templates/reviews/action_feed.html b/reviewboard/templates/reviews/action_feed.html
new file mode 100644
index 0000000000000000000000000000000000000000..486cbe3d3e96ccba71a3c8f1cd77752e89642edd
--- /dev/null
+++ b/reviewboard/templates/reviews/action_feed.html
@@ -0,0 +1,95 @@
+{% extends "base.html" %}
+{% load djblets_deco %}
+{% load i18n %}
+{% load reviewtags %}
+{% load staticfiles %}
+{% load djblets_deco %}
+{% load djblets_extensions %}
+{% load djblets_utils %}
+{% load rb_extensions %}
+{% load tz %}
+
+{% block title %}{% trans "Action feed" %}{% endblock %}
+
+{% block extrahead %}
+<meta http-equiv="refresh" content="300" />
+{% endblock %}
+
+{% block jsconsts %}
+{{block.super}}
+{%  include "reviews/review_flags.js" %}
+{% endblock %}
+
+{% block css %}
+{{block.super}}
+<link href="/static/rb/css/diffviewer.less" rel="stylesheet/less" type="text/css" />
+<link href="/static/rb/css/reviews.less" rel="stylesheet/less" type="text/css" />
+<link href="/static/rb/css/syntax.css" rel="stylesheet" type="text/css" />
+<link rel="stylesheet" type="text/css" href="{% static "djblets/css/datagrid.css" %}" />
+{% endblock %}
+
+{% block scripts-post %}
+<script type="text/javascript" src="{% static "djblets/js/datagrid.js" %}"></script>
+{% endblock %}
+
+{% block content %}
+
+{% box "dashboard" %}
+<div id="dashboard-wrapper">
+ <table class="datagrid" id="dashboard-navbar">
+  <colgroup>
+   <col class="summary" />
+   <col class="count" />
+  </colgroup>
+  <tbody>
+{% dashboard_entry "main-item" "Action Feed"      "action_feed" %}
+{% dashboard_entry "main-item" "Starred Reviews"  "starred" %}
+{% dashboard_entry "main-item" "Outgoing Reviews" "outgoing" %}
+{% dashboard_entry "main-item" "Incoming Reviews" "incoming" %}
+{% dashboard_entry "sub-item"  "To Me"            "to-me" %}
+{% for name, value in datagrid.counts.groups.items %}
+{%  dashboard_entry "sub-item" name "to-group" name %}
+{% endfor %}
+{% if datagrid.counts.starred_groups %}
+{%  dashboard_entry "main-item" "Watched Groups" "watched-groups" %}
+{%  for name, value in datagrid.counts.starred_groups.items %}
+{%   dashboard_entry "sub-item" name "to-group" name %}
+{%  endfor %}
+{% endif %}
+{% dashboard_entry "main-item" "All My Requests" "mine" %}
+{% for hook in sidebar_hooks %}
+{%  for main_entry in hook.entries %}
+{%   dashboard_entry "main-item" main_entry.label "url" main_entry.url %}
+{%   for sub_entry in main_entry.subitems %}
+{%    dashboard_entry "sub-item" sub_entry.label "url" sub_entry.url %}
+{%   endfor %}
+{%  endfor %}
+{% endfor %}
+  </tbody>
+ </table>
+ <div id="dashboard-main" class="clearfix">
+
+{% for activity in stream %}
+
+{% definevar "boxclass" %}review {{entry.class}}{% enddefinevar %}
+{% box boxclass %}
+
+<div class="main">
+ <div class="banners"></div>
+ <div class="header">
+  <div class="collapse-button"></div>
+  <div class="reviewer"><a href="{% url user activity.actor %}" class="user">{{activity.actor|user_displayname}}</a> {{activity.verb }} </div>
+  <div class="posted_time">{% localtime on %}{% blocktrans with activity.timestamp|timesince as timestamp_since  and activity.timestamp|date:"F jS, Y, P" as timestamp_date %}Posted {{ timestamp_since }} ago ({{ timestamp_date }}){% endblocktrans %}{% endlocaltime %}</div>
+ </div>
+
+ <div class="body">
+   <pre class="body_top reviewtext">{{ activity.target}}</pre>
+ </div><!-- body -->
+</div><!-- main -->
+{% endbox %}
+{% endfor %}
+
+ </div>
+</div>
+{% endbox %}
+{% endblock %}
diff --git a/reviewboard/templates/reviews/dashboard.html b/reviewboard/templates/reviews/dashboard.html
index c57bcfe80d5fdeef75ec2e269ff6caadec665de6..40854ca7d68bc2ac0926af2a1274d35d74d2ac29 100755
--- a/reviewboard/templates/reviews/dashboard.html
+++ b/reviewboard/templates/reviews/dashboard.html
@@ -28,6 +28,7 @@
    <col class="count" />
   </colgroup>
   <tbody>
+{% dashboard_entry "main-item" "Action Feed"      "action_feed" %}
 {% dashboard_entry "main-item" "Starred Reviews"  "starred" %}
 {% dashboard_entry "main-item" "Outgoing Reviews" "outgoing" %}
 {% dashboard_entry "main-item" "Incoming Reviews" "incoming" %}
diff --git a/reviewboard/urls.py b/reviewboard/urls.py
index 1af463a3438810f92917f6466a27d98a17c990ae..949f49191a8d2879c7a493ae11f4d6f6d9ef73e4 100644
--- a/reviewboard/urls.py
+++ b/reviewboard/urls.py
@@ -74,6 +74,8 @@ localsite_urlpatterns = patterns('',
     url(r'^dashboard/$',
         'reviewboard.reviews.views.dashboard', name="dashboard"),
 
+    url(r'^action_feed/$',
+        'reviewboard.reviews.views.action_feed', name="action_feed"),
     # Users
     url(r'^users/$',
         'reviewboard.reviews.views.submitter_list', name="all-users"),
@@ -96,6 +98,8 @@ localsite_urlpatterns = patterns('',
 urlpatterns += patterns('',
     (r'^account/', include('reviewboard.accounts.urls')),
 
+    ('^activity/', include('actstream.urls')),
+
     (r'^s/(?P<local_site_name>[A-Za-z0-9\-_.]+)/',
      include(localsite_urlpatterns)),
 )
