diff --git a/assets/javascripts/discourse/lib/babble.js.es6 b/assets/javascripts/discourse/lib/babble.js.es6
index 2dddca3..e1096e4 100644
--- a/assets/javascripts/discourse/lib/babble.js.es6
+++ b/assets/javascripts/discourse/lib/babble.js.es6
@@ -294,6 +294,8 @@ export default Ember.Object.create({
       post.created_at = moment(post.created_at.replace(' UTC', 'Z')).local().toString()
     }
 
+    post.yours = post.user_id == User.currentProp('id')
+
     if (data.is_edit || data.is_delete) {
       topic.postStream.storePost(post)
       if (topic.get('loadingEditId') == data.id) {
@@ -332,6 +334,14 @@ export default Ember.Object.create({
   handleTyping(topic, data) {
     if (data.id == User.currentProp('id')) { return }
     topic.typing[data.username] = { user: data, lastTyped: moment() }
+
+    forEachTopicContainer(topic, $container => {
+      const postNumber = lastVisibleElement($container.find('.babble-chat'), '.babble-post', 'post-number')
+      if (postNumber === topic.highest_post_number) {
+        scrollToPost(topic, postNumber, 400, 80)
+      }
+    })
+
     rerender(topic)
   },
 
diff --git a/assets/javascripts/discourse/templates/admin/chat.hbs b/assets/javascripts/discourse/templates/admin/chat.hbs
index a8ac947..3fd8bf1 100644
--- a/assets/javascripts/discourse/templates/admin/chat.hbs
+++ b/assets/javascripts/discourse/templates/admin/chat.hbs
@@ -39,7 +39,7 @@
     <div class='buttons'>
       <button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button>
       {{#if model.id}}
-        <button {{action "destroy"}} class='btn btn-danger'>{{d-icon "trash-o"}}{{i18n 'admin.customize.delete'}}</button>
+        <button {{action "destroy"}} class='btn btn-danger'>{{d-icon "far-trash-alt"}}{{i18n 'admin.customize.delete'}}</button>
       {{/if}}
     </div>
   </form>
diff --git a/assets/javascripts/discourse/widgets/templates/babble-chat.js.es6 b/assets/javascripts/discourse/widgets/templates/babble-chat.js.es6
index 83a6dfa..6851702 100644
--- a/assets/javascripts/discourse/widgets/templates/babble-chat.js.es6
+++ b/assets/javascripts/discourse/widgets/templates/babble-chat.js.es6
@@ -18,7 +18,6 @@ export default Ember.Object.create({
         h('ul', {className: 'babble-posts'}, this.chatView()),
         this.pressurePlate('asc')
       ]),
-      this.widget.attach('babble-typing', { topic: this.topic }),
       this.widget.attach('babble-composer', { topic: this.topic, csrf: this.csrf })
     ]
     if (!this.widget.attrs.fullpage) {
@@ -54,16 +53,13 @@ export default Ember.Object.create({
         break
     }
 
-    if (this.topic.loadingPosts) {
-      return h('span.babble-load-message', I18n.t('babble.loading_messages'))
-    } else if (canLoadMore) {
+    if (canLoadMore) {
       return this.widget.attach('button', {
-        label:     'babble.load_more',
+        label:     this.topic.loadingPosts ? 'babble.loading_messages' : 'babble.load_more',
         className: `babble-load-message babble-pressure-plate ${order}`,
+        disabled:  this.topic.loadingPosts,
         action:    actionName
       })
-    } else {
-      return h('span.babble-load-message', I18n.t('babble.no_more_messages'))
     }
   },
 
@@ -118,7 +114,7 @@ export default Ember.Object.create({
           isFollowOn: isFollowOn(post, posts[index-1]),
           isNewDay: isNewDay(post, posts[index-1])
         })
-      })
+      }).concat(this.widget.attach('babble-typing', { topic: this.topic }))
     } else {
       return h('li.babble-empty-topic-message', I18n.t('babble.empty_topic_message'))
     }
diff --git a/assets/javascripts/discourse/widgets/templates/babble-post-actions.js.es6 b/assets/javascripts/discourse/widgets/templates/babble-post-actions.js.es6
index 9a721b5..bff1a57 100644
--- a/assets/javascripts/discourse/widgets/templates/babble-post-actions.js.es6
+++ b/assets/javascripts/discourse/widgets/templates/babble-post-actions.js.es6
@@ -50,7 +50,7 @@ export default Ember.Object.create({
 
   delete() {
     if (this.post.can_delete) {
-      return this.widget.attach('link', { className: 'btn', icon: 'trash-o', action: 'delete', label: 'user.admin_delete' })
+      return this.widget.attach('link', { className: 'btn', icon: 'far-trash-alt', action: 'delete', label: 'user.admin_delete' })
     }
   }
 
diff --git a/assets/javascripts/discourse/widgets/templates/babble-post.js.es6 b/assets/javascripts/discourse/widgets/templates/babble-post.js.es6
index 373d61e..b18ccde 100644
--- a/assets/javascripts/discourse/widgets/templates/babble-post.js.es6
+++ b/assets/javascripts/discourse/widgets/templates/babble-post.js.es6
@@ -1,6 +1,6 @@
 import { h } from 'virtual-dom'
 import RawHtml from 'discourse/widgets/raw-html';
-import { relativeAge } from 'discourse/lib/formatter'
+import { relativeAge, longDate } from 'discourse/lib/formatter'
 import { avatarImg } from 'discourse/widgets/post'
 import { emojiUnescape } from 'discourse/lib/text'
 import { iconNode } from "discourse-common/lib/icon-library";
@@ -29,14 +29,12 @@ export default Ember.Object.create({
 
   contents() {
     return [
-      h('a.babble-avatar-wrapper', { attributes: {
-            'data-user-card': this.post.username,
-            'href': `/u/${this.post.username}`
-      } }, this.avatar()),
+      this.avatarWrapper(),
       h('div.babble-post-content-wrapper', [
         this.title(),
-        this.body()
-      ])
+        this.body(),
+      ]),
+      this.post.yours ? null : this.actions()
     ]
   },
 
@@ -59,13 +57,22 @@ export default Ember.Object.create({
     }
   },
 
+  avatarWrapper() {
+    if (this.post.yours) { return }
+
+    return h('a.babble-avatar-wrapper', { attributes: {
+      'data-user-card': this.post.username,
+      'href': `/u/${this.post.username}`
+    } }, this.avatar())
+  },
+
   avatar() {
     if (this.isFollowOn) {
       return h('div.babble-avatar-placeholder')
     } else if (this.post.user_id) {
       return avatarImg('medium', {template: this.post.avatar_template, username: this.post.username})
     } else {
-      return iconNode('trash-o', { class: 'deleted-user-avatar'} )
+      return iconNode('far-trash-alt', { class: 'deleted-user-avatar'} )
     }
   },
 
@@ -74,29 +81,42 @@ export default Ember.Object.create({
   },
 
   postDate() {
-    return h('div.babble-post-date', relativeAge(new Date(this.post.created_at)))
+    const timestamp = new Date(this.post.created_at)
+    return h('div.babble-post-date',
+      { attributes: { title: longDate(timestamp) } },
+      relativeAge(timestamp)
+    )
   },
 
   postEdited() {
-    if (!(this.post.self_edits > 0)) { return }
-    return h('div.babble-post-explainer', I18n.t('babble.post_edited'))
+    if (this.post.yours || !(this.post.self_edits > 0)) { return }
+
+    return h('div.babble-post-explainer',
+      { attributes: { title: I18n.t('babble.post_edited') } },
+      iconNode('pencil-alt')
+    )
   },
 
   postFlagged() {
     if (!this.post.has_flagged) { return }
-    return h('div.babble-post-explainer', `(${I18n.t('babble.flagged').toLowerCase()})`)
+
+    return h('div.babble-post-explainer',
+      { attributes: { title: I18n.t('babble.flagged') } },
+      iconNode('flag')
+    )
   },
 
   title() {
+    const actions = this.post.yours ? this.actions() : null
     if (this.isFollowOn) {
-      return h('div.babble-post-meta-data', this.actions())
+      return h('div.babble-post-meta-data', actions)
     } else {
       return h('div.babble-post-meta-data', [
         this.postName(),
         this.postDate(),
         this.postFlagged(),
         this.postEdited(),
-        this.actions()
+        actions
       ])
     }
   },
diff --git a/assets/javascripts/discourse/widgets/templates/babble-typing.js.es6 b/assets/javascripts/discourse/widgets/templates/babble-typing.js.es6
index ded654c..65e890f 100644
--- a/assets/javascripts/discourse/widgets/templates/babble-typing.js.es6
+++ b/assets/javascripts/discourse/widgets/templates/babble-typing.js.es6
@@ -1,22 +1,24 @@
 import { h } from 'virtual-dom'
+import { avatarImg } from 'discourse/widgets/post'
 
 export default Ember.Object.create({
   render(widget) {
     this.widget = widget
-    return h('div.babble-typing.clearfix', this.typingSentence(widget.state.topic.typing))
-  },
 
-  typingSentence(typing) {
-    let usernames = _.filter(_.keys(typing), function(username) {
-      return typing[username].lastTyped > moment().add(-1, 'second')
-    })
+    const typing = widget.state.topic.typing
+    const now = moment().add(-3, 'second')
 
-    if (!usernames.length) { return }
+    const typers = Object.values(widget.state.topic.typing).filter(({ lastTyped }) => lastTyped > now)
+    if (!typers.length) { return }
     setTimeout(() => { this.widget.scheduleRerender() }, 2000)
-    switch(usernames.length) {
-      case 1: return I18n.t("babble.typing.single", { first: usernames[0] })
-      case 2: return I18n.t("babble.typing.double", { first: usernames[0], second: usernames[1] })
-      case 3: return I18n.t("babble.typing.several")
-    }
+
+    return typers.map(({ user: { username, avatar_template } }) => (
+      h('div.babble-post-container.babble-typing-container', [
+        h('a.babble-avatar-wrapper', {
+          attributes: { 'data-user-card': username, 'href': `/u/${username}` }
+        }, avatarImg('medium', { username, template: avatar_template })),
+        h('div.babble-typing', [h('span'), h('span'), h('span')])
+      ])
+    ))
   }
 })
diff --git a/assets/stylesheets/babble.scss b/assets/stylesheets/babble.scss
index 0c51361..d053b38 100644
--- a/assets/stylesheets/babble.scss
+++ b/assets/stylesheets/babble.scss
@@ -126,6 +126,18 @@ $default-chat-width: 300px;
 /* Post styles */
 .babble-posts {
   margin: 0;
+  min-height: calc(100% - 10px);
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+
+  > :first-child {
+    margin-top: auto;
+  }
+
+  > :last-child {
+    margin-bottom: 8px;
+  }
 }
 
 .babble-post {
@@ -200,13 +212,25 @@ $default-chat-width: 300px;
   border-radius: 100%;
 }
 
+[data-my-post=true] .babble-post-container .babble-post-content-wrapper {
+  margin-left: auto;
+  background: dark-light-choose($tertiary-low, $tertiary-high);
+}
+
+
 .babble-post-container {
-  margin: 8px 0;
+  margin-top: 8px;
   position: relative;
   display: flex;
+  align-items: flex-start;
+
+  &.babble-typing-container {
+    align-items: center;
+    margin-bottom: 4px;
+  }
 
   &.babble-follow-on {
-    margin: 4px 0;
+    margin-top: 4px;
     .babble-post-content-wrapper {
       flex-direction: row;
       justify-content: space-between;
@@ -228,18 +252,29 @@ $default-chat-width: 300px;
 
   .babble-post-date {
     flex-grow: 1;
+    font-size: 14px;
   }
 
   .babble-post-explainer {
+    position: absolute;
+    height: 16px;
+    width: 16px;
     font-size: 12px;
-    flex-grow: 10;
+    top: 8px;
+    right: 4px;
+    opacity: 0.4;
   }
 
   .babble-post-content-wrapper {
+    background: dark-light-choose($primary-very-low, $primary-very-low);
+    border-radius: 5px;
+    max-width: 80%;
+    padding: 7px;
     display: flex;
     flex-direction: column;
     flex-grow: 1;
     word-break: break-word;
+    position: relative;
     a.mention {
       display: inline;
     }
@@ -254,6 +289,7 @@ $default-chat-width: 300px;
     min-height: 26px;
     display: flex;
     flex-direction: row;
+    align-items: baseline;
     justify-content: space-between;
   }
 
@@ -261,6 +297,15 @@ $default-chat-width: 300px;
     margin: 5px 0 0 0 !important;
   }
 
+  .babble-post-actions.closed {
+    opacity: 0;
+    transition: opacity 0.3s ease-in-out;
+  }
+
+  &:hover .babble-post-actions.closed {
+    opacity: 1;
+  }
+
   .babble-post-actions-menu {
     position: fixed;
     display: flex;
@@ -339,7 +384,37 @@ $default-chat-width: 300px;
   font-size: 12px;
   line-height: 14px;
   min-height: 14px;
-  padding: 5px 8px;
+  width: auto;
+  border-radius: 50px;
+  padding: 4px 0;
+  margin: 0 4px;
+  display: table;
+  position: relative;
+  span {
+    height: 8px;
+    width: 8px;
+    float: left;
+    margin-right: 4px;
+    background-color: #9E9EA1;
+    display: block;
+    border-radius: 50%;
+    opacity: 0.4;
+    position: relative;
+    top: 0;
+    @for $i from 1 through 3 {
+      &:nth-of-type(#{$i}) {
+        animation: 1s blink infinite ($i * .3333s);
+      }
+    }
+  }
+}
+
+@keyframes blink {
+  25% { top: 0; }
+  33% { top: 1px; }
+  50% { top: 2px; }
+  66% { top: 1px; }
+  75% { top: 0; }
 }
 
 /* Composer styles */
diff --git a/plugin.rb b/plugin.rb
index ae20500..02425b4 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -1,6 +1,6 @@
 # name: babble
 # about: Shoutbox plugin for Discourse
-# version: 4.1.3
+# version: 4.2.0
 # authors: James Kiesel (gdpelican)
 # url: https://github.com/gdpelican/babble