Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ GEM
activerecord (>= 6.0.0)
activesupport (>= 6.0.0)
ast (2.4.3)
avo (3.24.0)
avo (3.24.1)
actionview (>= 6.1)
active_link_to
activerecord (>= 6.1)
Expand Down Expand Up @@ -346,19 +346,19 @@ GEM
net-protocol
net-ssh (7.3.0)
nio4r (2.7.4)
nokogiri (1.18.9-aarch64-linux-gnu)
nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-aarch64-linux-musl)
nokogiri (1.18.10-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.9-arm-linux-gnu)
nokogiri (1.18.10-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-arm-linux-musl)
nokogiri (1.18.10-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.9-arm64-darwin)
nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-gnu)
nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-musl)
nokogiri (1.18.10-x86_64-linux-musl)
racc (~> 1.4)
oauth2 (2.0.15)
faraday (>= 0.17.3, < 4.0)
Expand Down
29 changes: 25 additions & 4 deletions app/components/menu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,34 @@ def view_template

if hotwire_native_app? || (@controller_name == "pages" && @action_name == "show")
if user_signed_in?
menu_link(path: destroy_user_session_path, text: t("navbar.sign_out"), icon: Hero::ArrowLeftStartOnRectangle.new(variant: :outline, class: "size-5 ltr:transform ltr:-scale-x-100"))
Separator(class: "my-2")
Link(
variant: :link,
href: destroy_user_session_path,
class: "flex items-center justify-start gap-x-2 text-xl text-muted-foreground",
data: {
controller: "bridge--sign-out",
bridge__sign_out_path_value: api_v1_auth_path,
action: [
("click->ruby-ui--sheet-content#close" if hotwire_native_app?),
"bridge--sign-out#signOut"
],
turbo_method: :delete
}
) do
Hero::ArrowLeftStartOnRectangle(variant: :outline, class: "size-5 ltr:transform ltr:-scale-x-100")

plain t("navbar.sign_out")
end
else
menu_link(path: new_user_session_path, text: t("navbar.sign_in"), icon: Hero::ArrowLeftStartOnRectangle.new(variant: :outline, class: "size-5 ltr:transform ltr:-scale-x-100"))
Separator(class: "my-2")
menu_link(
path: new_user_session_path,
text: t("navbar.sign_in"),
icon: Hero::ArrowLeftOnRectangle.new(variant: :outline, class: "size-5 ltr:transform ltr:-scale-x-100"),
)
end

Separator(class: "my-2")

if hotwire_native_app?
menu_link(
path: new_contact_path,
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/api/v1/auths_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Api::V1::AuthsController < ApplicationController
skip_before_action :verify_authenticity_token, only: [ :destroy ]

def destroy
sign_out(current_user)

render json: {}
end
end
10 changes: 10 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ class ApplicationController < ActionController::Base
# Only allow browsers supporting TailwindCSS v4.
allow_browser versions: { chrome: 111, safari: 16.4, firefox: 128 }, block: :handle_outdated_browser

before_action if: -> { devise_controller? && hotwire_native_app? } do
request.env["warden"].params["hotwire_native_form"] = true
end

before_action :check_rack_mini_profiler
before_action { Rails.error.set_context(request_url: request.original_url, params: params, session: session.inspect) }

private

def after_sign_in_path_for(resource_or_scope)
return "/reset_app" if hotwire_native_app?

super
end

def check_rack_mini_profiler
Rack::MiniProfiler.authorize_request if current_user&.admin?
end
Expand Down
63 changes: 63 additions & 0 deletions app/controllers/hotwire/ios/path_configurations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
class Hotwire::Ios::PathConfigurationsController < ApplicationController
def show
render json: {
settings: {
register_with_account: false,
require_authentication: false,
tabs: [
{
title: "الرئيسية",
path: "/",
ios_system_image_name: "house"
},
{
title: "التصنيفات",
path: "/categories",
ios_system_image_name: "square.grid.2x2"
},
{
title: "المؤلفون",
path: "/authors",
ios_system_image_name: "person.3"
},
{
title: "الكتب",
path: "/books",
ios_system_image_name: "books.vertical"
}
]
},
rules: [
{
patterns: [
"/new$",
"/edit$",
"/users/sign_up",
"/users/sign_in"
],
properties: {
context: "modal"
}
},
{
patterns: [ "^/unauthorized" ],
properties: {
view_controller: "unauthorized"
}
},
{
patterns: [ "^/reset_app$" ],
properties: {
view_controller: "reset_app"
}
},
{
patterns: [ "/?sign_in_token=.*" ],
properties: {
presentation: "replace"
}
}
]
}
end
end
2 changes: 1 addition & 1 deletion app/controllers/native/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ def handoff

sign_in(user)

redirect_to after_sign_in_path_for(user)
redirect_to "/reset_app"
end
end
8 changes: 8 additions & 0 deletions app/controllers/site_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class SiteController < ApplicationController
def reset_app
# Hotwire Native needs an empty page to route authentication and reset the app.
# We can't head: 200 because we also need the Turbo JavaScript in <head>.

render Views::Site::ResetApp.new
end
end
11 changes: 11 additions & 0 deletions app/controllers/static_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class StaticController < ApplicationController
include Devise::Controllers::Rememberable

CATEGORY_IDS = {
faith: [ 24, 34, 35, 36, 37, 38, 39, 40, 54 ],
quran: [ 1, 17, 20, 21, 22, 88, 101 ],
Expand All @@ -10,6 +12,15 @@ class StaticController < ApplicationController
}

def home
if params[:sign_in_token].present?
user = User.find_signed(params[:sign_in_token], purpose: "native_handoff")

if user.present?
sign_in(user)
remember_me(user)
end
end

results = search

if results.present? && params[:qid].blank? && request.headers["X-Sec-Purpose"] != "prefetch"
Expand Down
10 changes: 7 additions & 3 deletions app/controllers/users/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Users
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include Devise::Controllers::Rememberable

def google_oauth2 = process_oauth_callback("Google")

private
Expand All @@ -16,13 +18,15 @@ def process_oauth_callback(provider)

def handle_successful_authentication(user, provider)
sign_out_all_scopes
sign_in(user)

if native_oauth_request?
token = user.signed_id(purpose: "native_handoff", expires_in: 5.minutes)
token = user.signed_id(purpose: "native_handoff", expires_in: 30.seconds)

redirect_to handoff_native_session_url(token:)
redirect_to root_path(sign_in_token: token, locale: nil)
else
sign_in(user)
remember_me(user)

flash[:notice] = t("devise.omniauth_callbacks.success", kind: provider) if is_navigational_format?

redirect_to after_sign_in_path_for(user)
Expand Down
18 changes: 18 additions & 0 deletions app/javascript/controllers/bridge/sign_out_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"

// Connects to data-controller="bridge--sign-out"
export default class extends BridgeComponent {
static component = "sign-out"

static values = {
path: String,
}

signOut(event) {
event.preventDefault()
event.stopImmediatePropagation()

const path = this.pathValue
this.send("signOut", { path }, () => {})
}
}
3 changes: 3 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ application.register("bridge--menu-button", Bridge__MenuButtonController)
import Bridge__ShareController from "./bridge/share_controller"
application.register("bridge--share", Bridge__ShareController)

import Bridge__SignOutController from "./bridge/sign_out_controller"
application.register("bridge--sign-out", Bridge__SignOutController)

import Bridge__ThemeController from "./bridge/theme_controller"
application.register("bridge--theme", Bridge__ThemeController)

Expand Down
4 changes: 2 additions & 2 deletions app/views/devise/registrations/new.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def view_template

TextSeparator(text: t("or"), class: "my-2.5")

button_to(
omniauth_authorize_path(resource_name, :google_oauth2),
link_to(
omniauth_authorize_path(resource_name, :google_oauth2, native: hotwire_native_app? ? 1 : 0),
class: "inline-flex justify-center items-center py-2 px-5 w-full text-sm text-white rounded-md font-medium focus:outline-hidden bg-[#4285F4] hover:bg-[#4285F4]/90 gap-x-2 cursor-pointer",
data: { turbo: "false" }
) do
Expand Down
4 changes: 4 additions & 0 deletions app/views/site/reset_app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Views::Site::ResetApp < Views::Base
def view_template
end
end
27 changes: 23 additions & 4 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# frozen_string_literal: true

# Required to handle tabs that require authentication to present /unauthorized.
class TurboFailureApp < Devise::FailureApp
# Compatibility for Turbo::Native::Navigation
class << self
def helper_method(*methods)
end
end

include Turbo::Native::Navigation

# Intercept for Hotwire Native:
# Return a 401 for any :authenticate_user before actions
# Return a 422 for any login failures
def http_auth?
(hotwire_native_app? && !params["hotwire_native_form"]) || super
end
end

# Assuming you have not yet modified this file, each configuration option below
# is set to its default value. Note that some are commented out while others
# are not: uncommented lines are intended to protect your configuration from
Expand Down Expand Up @@ -285,10 +303,11 @@
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
config.warden do |manager|
manager.failure_app = TurboFailureApp
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
end

# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
Expand Down
13 changes: 13 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# GET /apple-app-site-association(.:format) rails/pwa#apple_app_site_association
# user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) users/omniauth_callbacks#passthru
# user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) users/omniauth_callbacks#google_oauth2
# reset_app GET /reset_app(.:format) site#reset_app
# pdfjs GET /pdfjs(.:format) pdfjs#index
# pdfjs_iframe GET /pdfjs/iframe(.:format) pdfjs#iframe
# /404(.:format) errors#not_found
Expand Down Expand Up @@ -50,7 +51,9 @@
# book_file GET (/:locale)/:book_id/:file_id(.:format) files#show {locale: /ar|ur|en/, book_id: /\d+/, file_id: /\d+/}
# book GET (/:locale)/:book_id(.:format) books#show {locale: /ar|ur|en/, book_id: /\d+/}
# handoff_native_session GET /native/session/handoff(.:format) native/sessions#handoff
# hotwire_ios_path_configuration GET /hotwire/ios/path_configuration(.:format) hotwire/ios/path_configurations#show
# privacy GET /privacy(.:format) static#privacy
# api_v1_auth DELETE /api/v1/auth(.:format) api/v1/auths#destroy
# api_v1_libraries GET /api/v1/libraries(.:format) api/v1/libraries#index
# api_v1_library GET /api/v1/libraries/:id(.:format) api/v1/libraries#show
# api_v1_books GET /api/v1/books(.:format) api/v1/books#index
Expand Down Expand Up @@ -327,6 +330,8 @@

devise_for :users, only: :omniauth_callbacks, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }

get "reset_app", to: "site#reset_app"

get "pdfjs", to: "pdfjs#index"
get "pdfjs/iframe", to: "pdfjs#iframe"

Expand Down Expand Up @@ -361,10 +366,18 @@
end
end

namespace :hotwire do
namespace :ios do
resource :path_configuration, only: :show
end
end

get "/privacy", to: "static#privacy", as: :privacy

namespace :api do
namespace :v1 do
resource :auth, only: [ :destroy ]

resources :libraries, only: %i[ index show ]
resources :books, only: %i[ index show ]
resources :authors, only: %i[ index show ]
Expand Down
Loading