Skip to content
Snippets Groups Projects
Commit a9e921f3 authored by Allen Gilliland's avatar Allen Gilliland
Browse files

Merge pull request #1555 from metabase/pulse_activity_items

Pulse activity items
parents 7b80aa5c ca9687ce
No related branches found
No related tags found
No related merge requests found
......@@ -51,10 +51,19 @@
transition: opacity 0.3s linear;
}
.PulseListItem {
overflow: hidden;
}
.PulseListItem:hover .PulseEditButton {
opacity: 1;
}
.PulseListItem.PulseListItem--focused {
border-color: #509EE3;
box-shadow: 0 0 3px #509EE3;
}
.DangerZone:hover {
border-color: var(--error-color);
transition: border .3s ease-in;
......
......@@ -151,6 +151,15 @@ export default class Activity extends Component {
description.userName = "Hello World!";
description.subject = "Metabase is up and running.";
break;
case "pulse-create":
description.subject = "created a pulse";
description.body = item.details.name;
description.bodyLink = (item.model_exists) ? Urls.modelToUrl(item.model, item.model_id) : null;
break;
case "pulse-delete":
description.subject = "deleted a pulse";
description.body = item.details.name;
break;
case "user-joined":
description.subject = "joined!";
break;
......
......@@ -13,10 +13,15 @@ var Urls = {
switch (model) {
case "card": return Urls.card(model_id);
case "dashboard": return Urls.dashboard(model_id);
case "pulse": return Urls.pulse(model_id);
default: return null;
}
},
pulse: function(pulse_id) {
return "/pulse/#"+pulse_id;
},
tableRowsQuery: function(database_id, table_id) {
return "/q/?db="+database_id+"&table="+table_id;
}
......
......@@ -55,6 +55,7 @@ export default class PulseList extends Component {
{pulses.slice().sort((a,b) => b.created_at - a.created_at).map(pulse =>
<li key={pulse.id}>
<PulseListItem
scrollTo={pulse.id === this.props.pulseId}
pulse={pulse}
user={user}
formInput={this.props.formInput}
......
import React, { Component, PropTypes } from "react";
import cx from "classnames";
import PulseListChannel from "./PulseListChannel.jsx";
export default class PulseListItem extends Component {
......@@ -9,11 +11,18 @@ export default class PulseListItem extends Component {
user: PropTypes.object.isRequired
};
componentDidMount() {
if (this.props.scrollTo) {
const element = React.findDOMNode(this.refs.pulseListItem);
element.scrollIntoView(true);
}
}
render() {
let { pulse, formInput, user } = this.props;
return (
<div className="PulseListItem bordered rounded mb2 pt3">
<div ref="pulseListItem" className={cx("PulseListItem bordered rounded mb2 pt3", {"PulseListItem--focused": this.props.scrollTo})}>
<div className="flex px4 mb2">
<div>
<h2 className="mb1">{pulse.name}</h2>
......
......@@ -27,6 +27,7 @@ Pulse.config(['$routeProvider', function ($routeProvider) {
$scope.Component = PulseListApp;
$scope.props = {
user: AppState.model.currentUser,
pulseId: parseInt($location.hash()),
onChangeLocation: function(url) {
$scope.$apply(() => $location.url(url));
}
......
......@@ -6,6 +6,7 @@
[metabase.db :as db]
[metabase.driver :as driver]
[metabase.email :as email]
[metabase.events :as events]
[metabase.integrations.slack :as slack]
(metabase.models [card :refer [Card]]
[database :refer [Database]]
......@@ -58,7 +59,10 @@
(defendpoint DELETE "/:id"
"Delete a `Pulse`."
[id]
(db/cascade-delete Pulse :id id))
(let [pulse (db/sel :one Pulse :id id)
result (db/cascade-delete Pulse :id id)]
(events/publish-event :pulse-delete (assoc pulse :actor_id *current-user-id*))
result))
(defendpoint GET "/form_input"
......
......@@ -20,6 +20,8 @@
:dashboard-add-cards
:dashboard-remove-cards
:install
:pulse-create
:pulse-delete
:user-login})
(def ^:private activity-feed-channel
......@@ -37,16 +39,16 @@
(let [{:keys [table-id database-id]} (when (fn? database-table-fn)
(database-table-fn object))]
(db/ins Activity
:topic topic
:user_id (events/object->user-id object)
:model (events/topic->model topic)
:model_id (events/object->model-id topic object)
:topic topic
:user_id (events/object->user-id object)
:model (events/topic->model topic)
:model_id (events/object->model-id topic object)
:database_id database-id
:table_id table-id
:custom_id (:custom_id object)
:details (if (fn? details-fn)
(details-fn object)
object))))
:table_id table-id
:custom_id (:custom_id object)
:details (if (fn? details-fn)
(details-fn object)
object))))
([topic object details-fn]
(record-activity topic object details-fn nil))
([topic object]
......@@ -95,6 +97,10 @@
; (assoc :status "completed")
; (dissoc :database_id :custom_id))))))))
(defn- process-pulse-activity [topic object]
(let [details-fn #(select-keys % [:name :public_perms])]
(record-activity topic object details-fn)))
(defn- process-user-activity [topic object]
;; we only care about login activity when its the users first session (a.k.a. new user!)
(when (and (= :user-login topic)
......@@ -116,6 +122,7 @@
"dashboard" (process-dashboard-activity topic object)
"install" (when-not (db/sel :one :fields [Activity :id])
(db/ins Activity :topic "install" :model "install"))
"pulse" (process-pulse-activity topic object)
"user" (process-user-activity topic object)))
(catch Throwable e
(log/warn (format "Failed to process activity event. %s" (:topic activity-event)) e))))
......
......@@ -6,6 +6,7 @@
[dashboard :refer [Dashboard]]
[database :refer [Database]]
[interface :refer :all]
[pulse :refer [Pulse]]
[table :refer [Table]]
[user :refer [User]])
[metabase.util :as u]))
......@@ -36,8 +37,9 @@
(assoc :table (delay (-> (Table table_id)
(select-keys [:id :name :display_name :description]))))
(assoc :model_exists (delay (case model
"card" (exists? Card :id model_id)
"card" (exists? Card :id model_id)
"dashboard" (exists? Dashboard :id model_id)
"pulse" (exists? Pulse :id model_id)
nil))))))
(extend-ICanReadWrite ActivityEntity :read :public-perms, :write :public-perms)
......@@ -3,6 +3,7 @@
[korma.core :as k]
[korma.db :as kdb]
[metabase.db :as db]
[metabase.events :as events]
(metabase.models [card :refer [Card]]
[common :refer [perms-readwrite]]
[hydrate :refer :all]
......@@ -134,11 +135,16 @@
(coll? channels)
(every? map? channels)]}
(kdb/transaction
;; update the pulse itself
(db/upd Pulse id :name name)
;; update cards (only if they changed)
(when (not= cards (db/sel :many :field [PulseCard :card_id] :pulse_id id (k/order :position :asc)))
(update-pulse-cards pulse cards))
;; update channels
(update-pulse-channels pulse channels)
(retrieve-pulse id)))
;; fetch the fully updated pulse and return it (and fire off an event)
(->> (retrieve-pulse id)
(events/publish-event :pulse-update))))
(defn create-pulse
"Create a new `Pulse` by inserting it into the database along with all associated pieces of data such as:
......@@ -161,4 +167,6 @@
(update-pulse-cards pulse cards)
;; add channels to the Pulse
(update-pulse-channels pulse channels)
(retrieve-pulse id))))
;; return the full Pulse (and record our create event)
(->> (retrieve-pulse id)
(events/publish-event :pulse-create)))))
......@@ -49,6 +49,7 @@
(cascade-delete 'Session :user_id id)
(cascade-delete 'Dashboard :creator_id id)
(cascade-delete 'Card :creator_id id)
(cascade-delete 'Pulse :creator_id id)
(cascade-delete 'Activity :user_id id)
(cascade-delete 'ViewLog :user_id id)))
......
......@@ -8,6 +8,7 @@
[dashboard :refer [Dashboard]]
[dashboard-card :refer [DashboardCard]]
[database :refer [Database]]
[pulse :refer [Pulse]]
[session :refer [Session]]
[user :refer [User]])
[metabase.test.data :refer :all]
......@@ -47,10 +48,15 @@
:visualization_settings {})
dashcard (db/ins DashboardCard
:card_id (:id card)
:dashboard_id (:id dashboard))]
:dashboard_id (:id dashboard))
pulse (db/ins Pulse
:creator_id (:id user)
:name rand-name
:public_perms 2)]
{:card card
:dashboard dashboard
:dashcard dashcard
:pulse pulse
:session {:id rand-name}
:user user}))
......@@ -237,6 +243,40 @@
(-> (db/sel :one Activity :topic "install")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:pulse-create` event
(expect-let [{:keys [pulse user]} (create-test-objects)]
{:topic :pulse-create
:user_id (:id user)
:model "pulse"
:model_id (:id pulse)
:database_id nil
:table_id nil
:details {:name (:name pulse)
:public_perms (:public_perms pulse)}}
(do
(k/delete Activity)
(process-activity-event {:topic :pulse-create
:item pulse})
(-> (db/sel :one Activity :topic "pulse-create")
(select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
;; `:pulse-delete` event
(expect-let [{:keys [pulse user]} (create-test-objects)]
{:topic :pulse-delete
:user_id (:id user)
:model "pulse"
:model_id (:id pulse)
:database_id nil
:table_id nil
:details {:name (:name pulse)
:public_perms (:public_perms pulse)}}
(do
(k/delete Activity)
(process-activity-event {:topic :pulse-delete
:item pulse})
(-> (db/sel :one Activity :topic "pulse-delete")
(select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
;; `:user-login` event
(expect-let [{{user-id :id} :user {session-id :id :as session} :session} (create-test-objects)]
{:topic :user-joined
......
(ns metabase.models.activity-test
(:require [expectations :refer :all]
[korma.core :refer [table]]
[metabase.events.activity-feed :refer :all]
[metabase.test.data.users :refer :all]))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment