Follows and some new notifications (#1063)

* all the good changes

* follows

* dont send duplicates

* remove follow_type for now

* dont add all the extra stuff we're not implementing yet

* refactor

* lots of fixes

* Delete activity.html.erb

* Delete activity.text.erb

* Update 20170209215819_create_follows.rb

* Update schema.rb

* Update mapping.rb

* Update mailboxer.rb
This commit is contained in:
Connor Turland 2017-02-11 00:20:42 -05:00 committed by GitHub
parent a647d80efa
commit b0deafc53e
29 changed files with 445 additions and 77 deletions

View file

@ -0,0 +1,13 @@
module MapMailerHelper
def access_approved_subject(map)
map.name + ' - access approved'
end
def access_request_subject(map)
map.name + ' - request to edit'
end
def invite_to_edit_subject(map)
map.name + ' - invited to edit'
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module TopicMailerHelper
def added_to_map_subject(topic, map)
topic.name + ' was added to map ' + map.name
end
def connected_subject(topic)
'new synapse to topic ' + topic.name
end
end

View file

@ -10,15 +10,24 @@ class ApplicationMailer < ActionMailer::Base
class << self
def mail_for_notification(notification)
case notification.notification_code
when MAILBOXER_CODE_ACCESS_REQUEST
when MAP_ACCESS_REQUEST
request = notification.notified_object
MapMailer.access_request(request)
when MAILBOXER_CODE_ACCESS_APPROVED
when MAP_ACCESS_APPROVED
request = notification.notified_object
MapMailer.access_approved(request)
when MAILBOXER_CODE_INVITE_TO_EDIT
when MAP_INVITE_TO_EDIT
user_map = notification.notified_object
MapMailer.invite_to_edit(user_map)
when TOPIC_ADDED_TO_MAP
event = notification.notified_object
TopicMailer.added_to_map(event, notification.recipients[0])
when TOPIC_CONNECTED_1
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic1, notification.recipients[0])
when TOPIC_CONNECTED_2
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic2, notification.recipients[0])
end
end
end

View file

@ -1,22 +1,23 @@
# frozen_string_literal: true
class MapMailer < ApplicationMailer
include MapMailerHelper
default from: 'team@metamaps.cc'
def access_request(request)
@request = request
@map = request.map
mail(to: @map.user.email, subject: request.requested_text)
end
def access_approved(request)
@request = request
@map = request.map
mail(to: request.user, subject: request.approved_text)
mail(to: request.user.email, subject: access_approved_subject(@map))
end
def access_request(request)
@request = request
@map = request.map
mail(to: @map.user.email, subject: access_request_subject(@map))
end
def invite_to_edit(user_map)
@inviter = user_map.map.user
@map = user_map.map
mail(to: user_map.user.email, subject: @map.invited_text)
mail(to: user_map.user.email, subject: invite_to_edit_subject(@map))
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
class TopicMailer < ApplicationMailer
include TopicMailerHelper
default from: 'team@metamaps.cc'
def added_to_map(event, user)
@entity = event.eventable
@event = event
mail(to: user.email, subject: added_to_map_subject(@entity, event.map))
end
def connected(synapse, topic, user)
@entity = topic
@event = synapse
mail(to: user.email, subject: connected_subject(topic))
end
end

View file

@ -27,14 +27,6 @@ class AccessRequest < ApplicationRecord
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
end
end
def requested_text
map.name + ' - request to edit'
end
def approved_text
map.name + ' - access approved'
end
protected

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
module Events
class TopicAddedToMap < Event
# after_create :notify_users!
after_create :notify_users!
def self.publish!(topic, map, user, meta)
create!(kind: 'topic_added_to_map',
@ -10,5 +10,12 @@ module Events
user: user,
meta: meta)
end
def notify_users!
# in the future, notify followers of both the topic, and the map
NotificationService.notify_followers(eventable, TOPIC_ADDED_TO_MAP, self)
# NotificationService.notify_followers(map, MAP_RECEIVED_TOPIC, self)
end
handle_asynchronously :notify_users!
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
module Events
class TopicUpdated < Event
# after_create :notify_users!
#after_create :notify_users!
def self.publish!(topic, user, meta)
create!(kind: 'topic_updated',
@ -9,5 +9,9 @@ module Events
user: user,
meta: meta)
end
def notify_users!
NotificationService.notify_followers(eventable, 'topic_updated', self)
end
end
end

19
app/models/follow.rb Normal file
View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
class Follow < ApplicationRecord
belongs_to :user
belongs_to :followed, polymorphic: true
has_one :follow_reason, dependent: :destroy
validates :user, presence: true
validates :followed, presence: true
validates :user, uniqueness: { scope: :followed, message: 'This entity is already followed by this user' }
after_create :add_subsetting
private
def add_subsetting
follow_reason = FollowReason.create!(follow: self)
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
class FollowReason < ApplicationRecord
REASONS = %w(created commented contributed followed shared_on starred).freeze
belongs_to :follow
validates :follow, presence: true
def has_reason
created || commented || contributed || followed || shared_on || starred
end
end

View file

@ -14,6 +14,8 @@ class Map < ApplicationRecord
has_many :synapses, through: :synapsemappings, source: :mappable, source_type: 'Synapse'
has_many :messages, as: :resource, dependent: :destroy
has_many :stars, dependent: :destroy
has_many :follows, as: :followed, dependent: :destroy
has_many :followers, :through => :follows, source: :user
has_many :access_requests, dependent: :destroy
has_many :user_maps, dependent: :destroy
@ -37,6 +39,7 @@ class Map < ApplicationRecord
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :screenshot, content_type: %r{\Aimage/.*\Z}
after_create :after_created_async
after_update :after_updated
after_save :update_deferring_topics_and_synapses, if: :permission_changed?
@ -135,15 +138,25 @@ class Map < ApplicationRecord
Synapse.where(defer_to_map_id: id).update(permission: permission)
end
def invited_text
name + ' - invited to edit'
end
protected
def after_created_async
FollowService.follow(self, self.user, 'created')
# notify users following the map creator
end
handle_asynchronously :after_created_async
def after_updated
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
ActionCable.server.broadcast 'map_' + id.to_s, type: 'mapUpdated'
end
def after_updated_async
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
# NotificationService.notify_followers(self, 'map_updated', changed_attributes)
# or better yet publish an event
end
end
handle_asynchronously :after_updated_async
end

View file

@ -14,7 +14,9 @@ class Mapping < ApplicationRecord
delegate :name, to: :user, prefix: true
after_create :after_created
after_create :after_created_async
after_update :after_updated
after_update :after_updated_async
before_destroy :before_destroyed
def user_image
@ -27,11 +29,10 @@ class Mapping < ApplicationRecord
def after_created
if mappable_type == 'Topic'
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicAdded', topic: mappable.filtered, mapping_id: id
meta = { 'x': xloc, 'y': yloc, 'mapping_id': id }
Events::TopicAddedToMap.publish!(mappable, map, user, meta)
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicAdded', topic: mappable.filtered, mapping_id: id
elsif mappable_type == 'Synapse'
Events::SynapseAddedToMap.publish!(mappable, map, user, meta)
ActionCable.server.broadcast(
'map_' + map.id.to_s,
type: 'synapseAdded',
@ -40,8 +41,14 @@ class Mapping < ApplicationRecord
topic2: mappable.topic2.filtered,
mapping_id: id
)
Events::SynapseAddedToMap.publish!(mappable, map, user, nil)
end
end
def after_created_async
FollowService.follow(map, user, 'contributed')
end
handle_asynchronously :after_created_async
def after_updated
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
@ -50,6 +57,13 @@ class Mapping < ApplicationRecord
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicMoved', id: mappable.id, mapping_id: id, x: xloc, y: yloc
end
end
def after_updated_async
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
FollowService.follow(map, updated_by, 'contributed')
end
end
handle_asynchronously :after_updated_async
def before_destroyed
if mappable.defer_to_map
@ -66,5 +80,6 @@ class Mapping < ApplicationRecord
Events::SynapseRemovedFromMap.publish!(mappable, map, updated_by, meta)
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseRemoved', id: mappable.id, mapping_id: id
end
FollowService.follow(map, updated_by, 'contributed')
end
end

View file

@ -6,6 +6,8 @@ class Message < ApplicationRecord
delegate :name, to: :user, prefix: true
after_create :after_created
#after_create :after_created_async
def user_image
user.image.url
@ -19,4 +21,10 @@ class Message < ApplicationRecord
def after_created
ActionCable.server.broadcast 'map_' + resource.id.to_s, type: 'messageCreated', message: as_json
end
def after_created_async
FollowService.follow(resource, user, 'commented')
NotificationService.notify_followers(resource, 'map_message', self)
end
handle_asynchronously :after_created_async
end

View file

@ -3,4 +3,19 @@ class Star < ActiveRecord::Base
belongs_to :user
belongs_to :map
validates :map, uniqueness: { scope: :user, message: 'You have already starred this map' }
#after_create :after_created_async
#before_destroy :before_destroyed
protected
def after_created_async
FollowService.follow(map, user, 'starred')
NotificationService.notify_followers(map, 'map_starred', self, 'created')
end
handle_asynchronously :after_created_async
def before_destroyed
FollowService.remove_reason(map, user, 'starred')
end
end

View file

@ -26,7 +26,9 @@ class Synapse < ApplicationRecord
}
before_create :set_perm_by_defer
after_create :after_created_async
after_update :after_updated
before_destroy :before_destroyed
delegate :name, to: :user, prefix: true
@ -72,6 +74,12 @@ class Synapse < ApplicationRecord
def set_perm_by_defer
permission = defer_to_map.permission if defer_to_map
end
def after_created_async
follow_ids = NotificationService.notify_followers(topic1, TOPIC_CONNECTED_1, self)
NotificationService.notify_followers(topic2, TOPIC_CONNECTED_2, self, nil, follow_ids)
end
handle_asynchronously :after_created_async
def after_updated
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
@ -85,4 +93,10 @@ class Synapse < ApplicationRecord
end
end
end
def before_destroyed
# hard to know how to do this yet, because the synapse actually gets destroyed
#NotificationService.notify_followers(topic1, 'topic_disconnected', self)
#NotificationService.notify_followers(topic2, 'topic_disconnected', self)
end
end

View file

@ -15,12 +15,17 @@ class Topic < ApplicationRecord
has_many :mappings, as: :mappable, dependent: :destroy
has_many :maps, through: :mappings
has_many :follows, as: :followed, dependent: :destroy
has_many :followers, :through => :follows, source: :user
belongs_to :metacode
before_create :set_perm_by_defer
before_create :create_metamap?
after_create :after_created_async
after_update :after_updated
after_update :after_updated_async
#before_destroy :before_destroyed
validates :permission, presence: true
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
@ -149,6 +154,12 @@ class Topic < ApplicationRecord
self.link = Rails.application.routes.url_helpers
.map_url(host: ENV['MAILER_DEFAULT_URL'], id: @map.id)
end
def after_created_async
FollowService.follow(self, self.user, 'created')
# notify users following the topic creator
end
handle_asynchronously :after_created_async
def after_updated
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
@ -162,4 +173,16 @@ class Topic < ApplicationRecord
end
end
end
def after_updated_async
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
end
end
handle_asynchronously :after_updated_async
def before_destroyed
# hard to know how to do this yet, because the topic actually gets destroyed
#NotificationService.notify_followers(self, 'topic_deleted', ?)
end
end

View file

@ -12,6 +12,10 @@ class User < ApplicationRecord
has_many :stars
has_many :user_maps, dependent: :destroy
has_many :shared_maps, through: :user_maps, source: :map
has_many :follows, as: :followed
has_many :followers, through: :follows, source: :user
has_many :following, class_name: 'Follow'
after_create :generate_code

View file

@ -5,6 +5,7 @@ class UserMap < ApplicationRecord
belongs_to :access_request
after_create :after_created_async
before_destroy :before_destroyed
def mark_invite_notifications_as_read
Mailboxer::Notification.where(notified_object: self).find_each do |notification|
@ -15,11 +16,17 @@ class UserMap < ApplicationRecord
protected
def after_created_async
FollowService.follow(map, user, 'shared_on')
if access_request
NotificationService.access_approved(self.access_request)
else
NotificationService.invite_to_edit(self)
end
# NotificationService.notify_followers(map, 'map_collaborator_added', self, 'shared_on')
end
handle_asynchronously :after_created_async
def before_destroyed
FollowService.remove_reason(map, user, 'shared_on')
end
end

View file

@ -0,0 +1,35 @@
# frozen_string_literal: true
class FollowService
def self.follow(entity, user, reason)
#return unless is_tester(user)
follow = Follow.where(followed: entity, user: user).first_or_create
if FollowReason::REASONS.include?(reason) && !follow.follow_reason.read_attribute(reason)
follow.follow_reason.update_attribute(reason, true)
end
end
def self.unfollow(entity, user)
Follow.where(followed: entity, user: user).destroy_all
end
def self.remove_reason(entity, user, reason)
return unless FollowReason::REASONS.include?(reason)
follow = Follow.where(followed: entity, user: user).first
if follow
follow.follow_reason.update_attribute(reason, false)
if !follow.follow_reason.has_reason
follow.destroy
end
end
end
protected
def is_tester(user)
%w(connorturland@gmail.com devin@callysto.com chessscholar@gmail.com solaureum@gmail.com ishanshapiro@gmail.com).include?(user.email)
end
end

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
class NotificationService
extend TopicMailerHelper
extend MapMailerHelper
# for strip_tags
include ActionView::Helpers::SanitizeHelper
@ -10,43 +12,68 @@ class NotificationService
)
end
def self.get_template_for_event_type(event_type)
'map_mailer/' + event_type
end
def self.get_mailboxer_code_for_event_type(event_type)
def self.get_settings_for_event(entity, event_type, event)
case event_type
when 'access_approved'
MAILBOXER_CODE_ACCESS_APPROVED
when 'access_request'
MAILBOXER_CODE_ACCESS_REQUEST
when 'invite_to_edit'
MAILBOXER_CODE_INVITE_TO_EDIT
when TOPIC_ADDED_TO_MAP
subject = added_to_map_subject(entity, event.map)
template = 'topic_mailer/added_to_map'
when TOPIC_CONNECTED_1
subject = connected_subject(event.topic1)
template = 'topic_mailer/connected'
when TOPIC_CONNECTED_2
subject = connected_subject(event.topic2)
template = 'topic_mailer/connected'
end
{template: template, subject: subject}
end
def self.send_for_follows(follows, entity, event_type, event)
return if follows.length == 0
settings = get_settings_for_event(entity, event_type, event)
# we'll prbly want to put the body into the actual loop so we can pass the current user in as a local
body = renderer.render(template: settings[:template], locals: { entity: entity, event: event }, layout: false)
follows.each{|follow|
# this handles email and in-app notifications, in the future, include push
follow.user.notify(settings[:subject], body, event, false, event_type, (follow.user.emails_allowed && follow.email), event.user)
# push could be handled with Actioncable to send transient notifications to the UI
# the receipt from the notify call could be used to link to the full notification
}
end
def self.notify_followers(entity, event_type, event, reason_filter = nil, exclude_follows = nil)
follows = entity.follows.where.not(user_id: event.user.id)
if !exclude_follows.nil?
follows = follows.where.not(id: exclude_follows)
end
if reason_filter.class == String && FollowReason::REASONS.include?(reason_filter)
follows = follows.joins(:follow_reason).where('follow_reasons.' + reason_filter => true)
elsif reason_filter.class == Array
# TODO: throw an error here if all the reasons aren't valid
follows = follows.joins(:follow_reason).where(reason_filter.map{|r| "follow_reasons.#{r} = 't'"}.join(' OR '))
end
send_for_follows(follows, entity, event_type, event)
return follows.map(&:id)
end
def self.access_request(request)
event_type = 'access_request'
template = get_template_for_event_type(event_type)
mailboxer_code = get_mailboxer_code_for_event_type(event_type)
body = renderer.render(template: template, locals: { map: request.map, request: request }, layout: false)
request.map.user.notify(request.requested_text, body, request, false, mailboxer_code, true, request.user)
subject = access_request_subject(request.map)
body = renderer.render(template: 'map_mailer/access_request', locals: { map: request.map, request: request }, layout: false)
request.map.user.notify(subject, body, request, false, MAP_ACCESS_REQUEST, true, request.user)
end
def self.access_approved(request)
event_type = 'access_approved'
template = get_template_for_event_type(event_type)
mailboxer_code = get_mailboxer_code_for_event_type(event_type)
body = renderer.render(template: template, locals: { map: request.map }, layout: false)
request.user.notify(request.approved_text, body, request, false, mailboxer_code, true, request.map.user)
subject = access_approved_subject(request.map)
body = renderer.render(template: 'map_mailer/access_approved', locals: { map: request.map }, layout: false)
request.user.notify(subject, body, request, false, MAP_ACCESS_APPROVED, true, request.map.user)
end
def self.invite_to_edit(user_map)
event_type = 'invite_to_edit'
template = get_template_for_event_type(event_type)
mailboxer_code = get_mailboxer_code_for_event_type(event_type)
body = renderer.render(template: template, locals: { map: user_map.map, inviter: user_map.map.user }, layout: false)
user_map.user.notify(user_map.map.invited_text, body, user_map, false, mailboxer_code, true, user_map.map.user)
subject = invite_to_edit_subject(user_map.map)
body = renderer.render(template: 'map_mailer/invite_to_edit', locals: { map: user_map.map, inviter: user_map.map.user }, layout: false)
user_map.user.notify(subject, body, user_map, false, MAP_INVITE_TO_EDIT, true, user_map.map.user)
end
# note: this is a global function, probably called from the rails console with some html body
@ -54,25 +81,9 @@ class NotificationService
users = opts[:users] || User.all
obj = opts[:obj] || nil
sanitize_text = opts[:sanitize_text] || false
notification_code = opts[:notification_code] || MAILBOXER_CODE_MESSAGE_FROM_DEVS
notification_code = opts[:notification_code] || MESSAGE_FROM_DEVS
send_mail = opts[:send_mail] || true
sender = opts[:sender] || User.find_by(email: 'ishanshapiro@gmail.com')
Mailboxer::Notification.notify_all(users, subject, body, obj, sanitize_text, notification_code, send_mail, sender)
end
def self.text_for_notification(notification)
case notification.notification_code
when MAILBOXER_CODE_ACCESS_APPROVED
map = notification.notified_object.map
'granted your request to edit map <span class="in-bold">' + map.name + '</span>'
when MAILBOXER_CODE_ACCESS_REQUEST
map = notification.notified_object.map
'wants permission to map with you on <span class="in-bold">' + map.name + '</span>&nbsp;&nbsp;<div class="action">Offer a response</div>'
when MAILBOXER_CODE_INVITE_TO_EDIT
map = notification.notified_object.map
'gave you edit access to map <span class="in-bold">' + map.name + '</span>'
when MAILBOXER_CODE_MESSAGE_FROM_DEVS
notification.subject
end
end
end

View file

@ -7,7 +7,7 @@
<h2 class="title">Notifications</h4>
</header>
<ul class="notifications">
<% blacklist = [MAILBOXER_CODE_ACCESS_REQUEST, MAILBOXER_CODE_ACCESS_APPROVED, MAILBOXER_CODE_INVITE_TO_EDIT] %>
<% blacklist = [MAP_ACCESS_REQUEST, MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT] %>
<% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%>
<% notifications.each do |notification| %>
<% receipt = @receipts.find_by(notification_id: notification.id) %>
@ -18,7 +18,34 @@
</div>
<div class="notification-body">
<div class="in-bold"><%= notification.sender.name %></div>
<%= raw NotificationService.text_for_notification(notification) %>
<%=
case notification.notification_code
when MAP_ACCESS_APPROVED
map = notification.notified_object.map
output = 'granted your request to edit map <span class="in-bold">' + map.name + '</span>'
when MAP_ACCESS_REQUEST
map = notification.notified_object.map
output = 'wants permission to map with you on <span class="in-bold">' + map.name + '</span>&nbsp;&nbsp;<div class="action">Offer a response</div>'
when MAP_INVITE_TO_EDIT
map = notification.notified_object.map
output = 'gave you edit access to map <span class="in-bold">' + map.name + '</span>'
when TOPIC_ADDED_TO_MAP
topic = notification.notified_object.eventable
map = notification.notified_object.map
output = 'added topic <span class="in-bold">' + topic.name + '</span> to map <span class="in-bold">' + map.name + '</span>'
when TOPIC_CONNECTED_1
topic1 = notification.notified_object.topic1
topic2 = notification.notified_object.topic2
output = 'connected <span class="in-bold">' + topic1.name + '</span> to <span class="in-bold">' + topic2.name + '</span>'
when TOPIC_CONNECTED_2
topic1 = notification.notified_object.topic1
topic2 = notification.notified_object.topic2
output = 'connected <span class="in-bold">' + topic2.name + '</span> to <span class="in-bold">' + topic1.name + '</span>'
when MESSAGE_FROM_DEVS
output = notification.subject
end
raw output
%>
</div>
<% end %>
<div class="notification-read-unread">

View file

@ -0,0 +1,11 @@
<% topic = @entity || entity %>
<% event = @event || event %>
<% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %>
<p>
<span style="font-weight: bold;"><%= event.user.name %></span>
added topic <span style="font-weight: bold"><%= topic.name %></span>
to map <span style="font-weight: bold"><%= event.map.name %></span>
</p>
<%= link_to 'Go to Topic', topic_url(topic), style: button_style %>
<%= link_to 'Go to Map', map_url(event.map), style: button_style %>

View file

@ -0,0 +1,6 @@
<% topic = @entity || entity %>
<% event = @event || event %>
<%= event.user.name %> added topic <%= topic.name %> to map <%= event.map.name %>
topic_url(topic)
map_url(event.map)

View file

@ -0,0 +1,15 @@
<% topic1 = @entity || entity
synapse = @event || event
button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none"
topic2 = (topic1.id == synapse.topic1_id ? synapse.topic2 : synapse.topic1) %>
<p>
<span style="font-weight: bold;"><%= synapse.user.name %></span>
connected topic <span style="font-weight: bold"><%= topic1.name %></span>
to topic <span style="font-weight: bold"><%= topic2.name %></span>
<% if synapse.desc.length > 0 %>
with the description "<%= synapse.desc %>".
<% end %>
</p>
<%= link_to 'View the connection', topic_url(topic1), style: button_style %>

View file

@ -0,0 +1,8 @@
<% topic1 = @entity || entity
synapse = @event || event
topic2 = (topic1.id == synapse.topic1_id ? synapse.topic2 : synapse.topic1) %>
<%= synapse.user.name %> connected topic <%= topic1.name %> to topic <%= topic2.name %>
<%= synapse.desc.length > 0 ? ' with the description' + event.desc : '' %>
<%= topic_url(topic1) %>

View file

@ -7,10 +7,36 @@
# notification_code: MAILBOXER_CODE_ACCESS_REQUEST
# },
# which would imply that this is an access request to Map.find(1)
MAILBOXER_CODE_ACCESS_REQUEST = 'ACCESS_REQUEST'
MAILBOXER_CODE_ACCESS_APPROVED = 'ACCESS_APPROVED'
MAILBOXER_CODE_INVITE_TO_EDIT = 'INVITE_TO_EDIT'
MAILBOXER_CODE_MESSAGE_FROM_DEVS = 'MESSAGE_FROM_DEVS'
MESSAGE_FROM_DEVS = 'MESSAGE_FROM_DEVS'
# these ones are old and can't change without a migration
MAP_ACCESS_APPROVED = 'ACCESS_APPROVED'
MAP_ACCESS_REQUEST = 'ACCESS_REQUEST'
MAP_INVITE_TO_EDIT = 'INVITE_TO_EDIT'
# these ones are new
# this one's a catch all for occurences on the map
# MAP_ACTIVITY = 'MAP_ACTIVITY'
# MAP_RECEIVED_TOPIC
# MAP_LOST_TOPIC
# MAP_TOPIC_MOVED
# MAP_RECEIVED_SYNAPSE
# MAP_LOST_SYNAPSE
# MAP_COLLABORATOR_ADDED
# MAP_UPDATED
# ADD_MAP_FORKED
# MAP_ADDED_TO_MAP
# MAP_MESSAGE
# MAP_STARRED
# MAP_COLLABORATOR_REMOVED
TOPIC_ADDED_TO_MAP = 'TOPIC_ADDED_TO_MAP'
TOPIC_CONNECTED_1 = 'TOPIC_CONNECTED_1'
TOPIC_CONNECTED_2 = 'TOPIC_CONNECTED_2'
# TOPIC_DELETED
# TOPIC_DISCONNECTED
# TOPIC_UPDATED
# TOPIC_REMOVED_FROM_MAP
Mailboxer.setup do |config|
# Configures if your application uses or not email sending for Notifications and Messages

View file

@ -0,0 +1,9 @@
class CreateFollows < ActiveRecord::Migration[5.0]
def change
create_table :follows do |t|
t.references :user, index: true
t.references :followed, polymorphic: true, index: true
t.timestamps
end
end
end

View file

@ -0,0 +1,14 @@
class CreateFollowReasons < ActiveRecord::Migration[5.0]
def change
create_table :follow_reasons do |t|
t.references :follow, index: true
t.boolean :created
t.boolean :contributed
t.boolean :commented
t.boolean :followed
t.boolean :shared_on
t.boolean :starred
t.timestamps
end
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170208161305) do
ActiveRecord::Schema.define(version: 20170209215911) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -67,6 +67,29 @@ ActiveRecord::Schema.define(version: 20170208161305) do
t.index ["user_id"], name: "index_events_on_user_id", using: :btree
end
create_table "follow_reasons", force: :cascade do |t|
t.integer "follow_id"
t.boolean "created"
t.boolean "contributed"
t.boolean "commented"
t.boolean "followed"
t.boolean "shared_on"
t.boolean "starred"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["follow_id"], name: "index_follow_reasons_on_follow_id", using: :btree
end
create_table "follows", force: :cascade do |t|
t.integer "user_id"
t.string "followed_type"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["followed_type", "followed_id"], name: "index_follows_on_followed_type_and_followed_id", using: :btree
t.index ["user_id"], name: "index_follows_on_user_id", using: :btree
end
create_table "in_metacode_sets", force: :cascade do |t|
t.integer "metacode_id"
t.integer "metacode_set_id"