Skip to content

Commit

Permalink
Add notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Freika committed Jul 4, 2024
1 parent 09152b5 commit bb2beb5
Show file tree
Hide file tree
Showing 33 changed files with 589 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .app_version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.8.2
0.8.3
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [0.8.1] — 2024-06-30
## [0.8.3] — 2024-07-03

### Added

- Notifications system. Now you will receive a notification when an import or export is finished, when stats update is completed and if any error occurs during any of these processes. Notifications are displayed in the top right corner of the screen and are stored in the database. You can see all your notifications on the Notifications page.
- Swagger API docs for /api/v1/owntracks/points You can find the API docs at `/api-docs`.

---

## [0.8.2] — 2024-06-30

### Added

Expand Down
2 changes: 1 addition & 1 deletion app/assets/builds/tailwind.css

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@
class ApplicationController < ActionController::Base
include Pundit::Authorization

before_action :unread_notifications

protected

def unread_notifications
return [] unless current_user

@unread_notifications ||= Notification.where(user: current_user).unread
end

def authenticate_api_key
return head :unauthorized unless current_api_user

Expand Down
23 changes: 23 additions & 0 deletions app/controllers/notifications_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

class NotificationsController < ApplicationController
before_action :authenticate_user!
before_action :set_notification, only: %i[show destroy]

def index
@notifications = current_user.notifications.paginate(page: params[:page], per_page: 25)
end

def show; end

def destroy
@notification.destroy!
redirect_to notifications_url, notice: 'Notification was successfully destroyed.', status: :see_other
end

private

def set_notification
@notification = Notification.find(params[:id])
end
end
14 changes: 14 additions & 0 deletions app/jobs/import_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,21 @@ def perform(user_id, import_id)
raw_points: result[:raw_points], doubles: result[:doubles], processed: result[:processed]
)

Notifications::Create.new(
user:,
kind: :info,
title: 'Import finished',
content: "Import \"#{import.name}\" successfully finished."
).call

StatCreatingJob.perform_later(user_id)
rescue StandardError => e
Notifications::Create.new(
user:,
kind: :error,
title: 'Import failed',
content: "Import \"#{import.name}\" failed: #{e.message}"
).call
end

private
Expand Down
3 changes: 1 addition & 2 deletions app/jobs/owntracks/point_creating_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
class Owntracks::PointCreatingJob < ApplicationJob
queue_as :default

# TODO: after deprecation of old endpoint, make user_id required
def perform(point_params, user_id = nil)
def perform(point_params, user_id)
parsed_params = OwnTracks::Params.new(point_params).call

return if point_exists?(parsed_params, user_id)
Expand Down
11 changes: 11 additions & 0 deletions app/models/notification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class Notification < ApplicationRecord
belongs_to :user

validates :title, :content, :kind, presence: true

enum kind: { info: 0, warning: 1, error: 2 }

scope :unread, -> { where(read_at: nil) }
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class User < ApplicationRecord
has_many :stats, dependent: :destroy
has_many :tracked_points, class_name: 'Point', dependent: :destroy
has_many :exports, dependent: :destroy
has_many :notifications, dependent: :destroy

after_create :create_api_key

Expand Down
4 changes: 4 additions & 0 deletions app/services/create_stats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def call
stat.save
end
end

Notifications::Create.new(user:, kind: :info, title: 'Stats updated', content: 'Stats updated').call
rescue StandardError => e
Notifications::Create.new(user:, kind: :error, title: 'Stats update failed', content: e.message).call
end
end

Expand Down
14 changes: 14 additions & 0 deletions app/services/exports/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,23 @@ def call
File.open(file_path, 'w') { |file| file.write(data) }

export.update!(status: :completed, url: "exports/#{export.name}.json")

Notifications::Create.new(
user:,
kind: :info,
title: 'Export finished',
content: "Export \"#{export.name}\" successfully finished."
).call
rescue StandardError => e
Rails.logger.error("====Export failed to create: #{e.message}")

Notifications::Create.new(
user:,
kind: :error,
title: 'Export failed',
content: "Export \"#{export.name}\" failed: #{e.message}"
).call

export.update!(status: :failed)
end

Expand Down
16 changes: 16 additions & 0 deletions app/services/notifications/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class Notifications::Create
attr_reader :user, :kind, :title, :content

def initialize(user:, kind:, title:, content:)
@user = user
@kind = kind
@title = title
@content = content
end

def call
Notification.create!(user:, kind:, title:, content:)
end
end
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<link href="https://cdn.jsdelivr.net/npm/daisyui@4.11.1/dist/full.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />

<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/map/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<% content_for :title, 'Map' %>

<div class='w-4/5 mt-10'>
<div class='w-4/5 mt-8'>
<div class="flex flex-col space-y-4 mb-4 w-full">
<%= form_with url: map_path, method: :get do |f| %>
<div class="flex flex-col md:flex-row md:space-x-4 md:items-end">
Expand Down Expand Up @@ -55,7 +55,7 @@
</div>

</div>
<div class='w-1/5 mt-10'>
<div class='w-1/5 mt-8'>
<%= render 'shared/right_sidebar' %>
</div>

Expand Down
17 changes: 17 additions & 0 deletions app/views/notifications/_notification.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div role="<%= notification.kind %>" class="<%= notification.kind %> shadow-lg" id="<%= dom_id notification %>">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-info h-6 w-6 shrink-0">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold"><%= link_to notification.title, notification, class: 'link hover:no-underline' %></h3>
<div class="text-s"><%= time_ago_in_words notification.created_at %> ago</div>
</div>
</div>
12 changes: 12 additions & 0 deletions app/views/notifications/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<% content_for :title, "Notifications" %>
<div class="w-full">
<div class="flex justify-between items-center">
<h1 class="font-bold text-4xl">Notifications</h1>
</div>

<div id="notifications" class="min-w-full">
<% @notifications.each do |notification| %>
<%= render notification %>
<% end %>
</div>
</div>
14 changes: 14 additions & 0 deletions app/views/notifications/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<%= render @notification %>
<%= link_to "Back to notifications", notifications_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to "Destroy this notification", @notification, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
</div>
</div>
</div>
59 changes: 45 additions & 14 deletions app/views/shared/_navbar.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="navbar bg-base-100 mb-5">
<div class="navbar bg-base-100">
<div class="navbar-start">
<div class="dropdown">
<label tabindex="0" class="btn btn-ghost lg:hidden">
Expand Down Expand Up @@ -46,22 +46,53 @@
</ul>
</div>
<div class="navbar-end">
<%# menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52 %>
<ul class="menu menu-horizontal bg-base-100 rounded-box px-1">
<% if user_signed_in? %>
<li>
<details>
<summary>
<%= "#{current_user.email}" %>
</summary>
<ul class="p-2 bg-base-100 rounded-t-none z-10">
<li><%= link_to 'Account', edit_user_registration_path %></li>
<li><%= link_to 'Settings', settings_path %></li>
<li><%= link_to 'Logout', destroy_user_session_path, method: :delete, data: { turbo_method: :delete } %></li>
<div class="dropdown dropdown-end dropdown-bottom">
<div tabindex="0" role="button" class='btn btn-sm btn-ghost hover:btn-ghost'>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
<% if @unread_notifications.present? %>
<span class="badge badge-xs badge-primary"></span>
<% end %>
</div>
<ul tabindex="0" class="dropdown-content z-100 menu p-2 shadow-lg bg-base-100 rounded-box min-w-52">
<% if @unread_notifications.any? %>
<li><%= link_to 'See all', notifications_path %></li>
<div class="divider p-0 m-0"></div>
<% end %>
<% @unread_notifications.first(10).each do |notification| %>
<li>
<a>
<%= notification.title %>
<span class="badge badge-xs justify-self-end badge-<%= notification.kind %>"></span>
</a>
</li>
<% end %>
</ul>
</details>
</li>
</details>
</div>
<li>
<details>
<summary>
<%= "#{current_user.email}" %>
</summary>
<ul class="p-2 bg-base-100 rounded-t-none z-10">
<li><%= link_to 'Account', edit_user_registration_path %></li>
<li><%= link_to 'Settings', settings_path %></li>
<li><%= link_to 'Logout', destroy_user_session_path, method: :delete, data: { turbo_method: :delete } %></li>
</ul>
</details>
</li>
<% else %>
<li><%= link_to 'Login', new_user_session_path %></li>
<% end %>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
delete :bulk_destroy
end
end
resources :notifications, only: %i[index show destroy]
resources :stats, only: :index do
collection do
post :update
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20240703105734_create_notifications.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateNotifications < ActiveRecord::Migration[7.1]
def change
create_table :notifications do |t|
t.string :title, null: false
t.text :content, null: false
t.references :user, null: false, foreign_key: true
t.integer :kind, null: false, default: 0
t.datetime :read_at

t.timestamps
end
add_index :notifications, :kind
end
end
29 changes: 28 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions spec/factories/notifications.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

FactoryBot.define do
factory :notification do
title { "MyString" }
content { "MyText" }
user
kind { :info }
read_at { nil }
end
end
Loading

0 comments on commit bb2beb5

Please sign in to comment.