Skip to content

Commit

Permalink
Merge pull request #100 from Freika/notifications
Browse files Browse the repository at this point in the history
Notifications
  • Loading branch information
Freika authored Jul 4, 2024
2 parents 0e94089 + 6748bcf commit 47f3e55
Show file tree
Hide file tree
Showing 34 changed files with 608 additions and 44 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
30 changes: 30 additions & 0 deletions app/controllers/notifications_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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.order(created_at: :desc).paginate(page: params[:page], per_page: 20)
end

def show; end

def mark_as_read
current_user.notifications.unread.update_all(read_at: Time.zone.now)

redirect_to notifications_url, notice: 'All notifications marked as read.', status: :see_other
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/imports/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@

<%= render "form", import: @import %>
<%= link_to "Back to imports", imports_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Back to imports", imports_path, class: "btn mx-5 mb-5" %>
</div>
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
22 changes: 22 additions & 0 deletions app/views/notifications/_notification.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div role="<%= notification.kind %>" class="<%= notification.kind %> shadow-lg p-5 flex justify-between items-center mb-4 rounded-lg bg-neutral" id="<%= dom_id notification %>">
<div class="flex-1">
<h3 class="font-bold text-xl">
<%= link_to notification.title, notification, class: 'link hover:no-underline text-blue-600' %>
</h3>
<div class="text-sm text-gray-500"><%= time_ago_in_words notification.created_at %> ago</div>

<% if params[:action] == 'show' %>
<div class="mt-2">
<%= notification.content %>
<% if notification.error? %>
<div class="mt-2">
Please, when reporting a bug to <a href="https://github.com/Freika/dawarich/issues" class="link hover:no-underline text-blue-600">Github Issues</a>, don't forget to include logs from <code>dawarich_app</code> and <code>dawarich_sidekiq</code> docker containers. Thank you!
</div>
<% end %>
</div>
<% end %>
</div>
<div class="badge badge-<%= notification.kind %> gap-2">
</div>
</div>
17 changes: 17 additions & 0 deletions app/views/notifications/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<% content_for :title, "Notifications" %>
<div class="flex flex-col items-center w-full">
<div class="text-center mb-6">
<h1 class="font-bold text-4xl mb-4">Notifications</h1>
<div class="flex items-center justify-center mb-4">
<%= link_to "Mark all as read", mark_notifications_as_read_path, method: :post, data: { turbo_method: :post }, class: "btn btn-sm btn-primary" %>
</div>
<div class="mb-4">
<%= will_paginate @notifications %>
</div>
</div>
<div id="notifications" class="w-full max-w-2xl">
<% @notifications.each do |notification| %>
<%= render notification %>
<% end %>
</div>
</div>
12 changes: 12 additions & 0 deletions app/views/notifications/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<%= render @notification %>

<div class='my-5'>
<%= link_to "Back to notifications", notifications_path, class: "btn btn-small" %>
<div class="inline-block ml-2">
<%= button_to "Destroy this notification", @notification, data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?", turbo_method: :delete }, method: :delete, class: "btn btn-small btn-warning" %>
</div>
</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-10 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>
<%= link_to notification do %>
<%= notification.title %>
<div class="badge badge-xs justify-self-end badge-<%= notification.kind %>"></div>
<% end %>
</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
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
delete :bulk_destroy
end
end
resources :notifications, only: %i[index show destroy]
post 'notifications/mark_as_read', to: 'notifications#mark_as_read', as: :mark_notifications_as_read
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
15 changes: 14 additions & 1 deletion db/schema.rb

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

Loading

0 comments on commit 47f3e55

Please sign in to comment.