-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add test page showing modals, flash messages and more * feat: better error handling * feat: add modal and error modal * refactor: refactoring
- Loading branch information
Showing
15 changed files
with
625 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Action; | ||
|
||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Slim\Exception\HttpInternalServerErrorException; | ||
use Slim\Exception\HttpBadRequestException; | ||
use Slim\Exception\HttpForbiddenException; | ||
use Slim\Exception\HttpGoneException; | ||
use Slim\Exception\HttpMethodNotAllowedException; | ||
use Slim\Exception\HttpNotFoundException; | ||
use Slim\Exception\HttpNotImplementedException; | ||
use Slim\Exception\HttpUnauthorizedException; | ||
|
||
final class ErrorAction | ||
{ | ||
public function __invoke( | ||
ServerRequestInterface $request, | ||
ResponseInterface $response, | ||
array $args | ||
): ResponseInterface { | ||
$type = (int)$args['type']; | ||
|
||
switch ($type) { | ||
case 400: | ||
throw new HttpBadRequestException($request); | ||
case 401: | ||
throw new HttpUnauthorizedException($request); | ||
case 403: | ||
throw new HttpForbiddenException($request); | ||
case 404: | ||
default: | ||
throw new HttpNotFoundException($request); | ||
case 405: | ||
throw new HttpMethodNotAllowedException($request); | ||
case 410: | ||
throw new HttpGoneException($request); | ||
case 500: | ||
throw new HttpInternalServerErrorException($request); | ||
case 501: | ||
throw new HttpNotImplementedException($request); | ||
case 600: | ||
trigger_error('Run-time notices', E_USER_NOTICE); | ||
break; | ||
case 601: | ||
trigger_error('Run-time warnings (non-fatal errors)', E_USER_WARNING); | ||
break; | ||
case 602: | ||
trigger_error('Fatal run-time error', E_USER_ERROR); | ||
break; | ||
} | ||
|
||
return $response->withHeader('Content-Type', 'application/json'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Handler; | ||
|
||
use Throwable; | ||
|
||
class JsonErrorRenderer extends \Slim\Error\AbstractErrorRenderer | ||
{ | ||
/** | ||
* @param Throwable $exception | ||
* @param bool $displayErrorDetails | ||
* @return string | ||
*/ | ||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string | ||
{ | ||
$error = [ | ||
'message' => $this->getErrorTitle($exception), | ||
'description' => $this->getErrorDescription($exception) | ||
]; | ||
|
||
if ($displayErrorDetails) { | ||
$error['exception'] = []; | ||
do { | ||
$error['exception'][] = $this->formatExceptionFragment($exception); | ||
} while ($exception = $exception->getPrevious()); | ||
} | ||
|
||
return (string) json_encode($error, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); | ||
} | ||
|
||
/** | ||
* @param Throwable $exception | ||
* @return array<string|int> | ||
*/ | ||
private function formatExceptionFragment(Throwable $exception): array | ||
{ | ||
return [ | ||
'type' => get_class($exception), | ||
'code' => $exception->getCode(), | ||
'message' => $exception->getMessage(), | ||
'file' => $exception->getFile(), | ||
'line' => $exception->getLine(), | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Handler; | ||
|
||
use Psr\Http\Message\ServerRequestInterface as Request; | ||
use Slim\Exception\HttpInternalServerErrorException; | ||
use Slim\ResponseEmitter; | ||
use Slim\Handlers\ErrorHandler; | ||
|
||
class ShutdownHandler | ||
{ | ||
/** | ||
* @var Request | ||
*/ | ||
private $request; | ||
|
||
/** | ||
* @var HttpErrorHandler | ||
*/ | ||
private $errorHandler; | ||
|
||
/** | ||
* @var bool | ||
*/ | ||
private $displayErrorDetails; | ||
|
||
/** | ||
* ShutdownHandler constructor. | ||
* | ||
* @param Request $request | ||
* @param HttpErrorHandler $errorHandler | ||
* @param bool $displayErrorDetails | ||
*/ | ||
public function __construct(Request $request, ErrorHandler $errorHandler, bool $displayErrorDetails) | ||
{ | ||
$this->request = $request; | ||
$this->errorHandler = $errorHandler; | ||
$this->displayErrorDetails = $displayErrorDetails; | ||
} | ||
|
||
public function __invoke() | ||
{ | ||
$error = error_get_last(); | ||
if ($error) { | ||
$errorFile = $error['file']; | ||
$errorLine = $error['line']; | ||
$errorMessage = $error['message']; | ||
$errorType = $error['type']; | ||
$message = 'An error while processing your request. Please try again later.'; | ||
|
||
if ($this->displayErrorDetails) { | ||
switch ($errorType) { | ||
case E_USER_ERROR: | ||
$message = "FATAL ERROR: {$errorMessage}. "; | ||
$message .= " on line {$errorLine} in file {$errorFile}."; | ||
break; | ||
|
||
case E_USER_WARNING: | ||
$message = "WARNING: {$errorMessage}"; | ||
break; | ||
|
||
case E_USER_NOTICE: | ||
$message = "NOTICE: {$errorMessage}"; | ||
break; | ||
|
||
default: | ||
$message = "ERROR: {$errorMessage}"; | ||
$message .= " on line {$errorLine} in file {$errorFile}."; | ||
break; | ||
} | ||
} | ||
|
||
$exception = new HttpInternalServerErrorException($this->request, $message); | ||
$response = $this->errorHandler->__invoke($this->request, $exception, $this->displayErrorDetails, false, false); | ||
|
||
if (ob_get_length()) { | ||
ob_clean(); | ||
} | ||
|
||
$responseEmitter = new ResponseEmitter(); | ||
$responseEmitter->emit($response); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import m from 'mithril' | ||
|
||
/** | ||
@typedef {{ | ||
// Title render function. | ||
// Using a function allows us to keep content | ||
// updated after the modal is opened. | ||
title(): any | ||
// Body render function | ||
body?(): any | ||
redraw?: boolean | ||
onclick?(id: string, event: any): void, | ||
onclose?(): void | ||
}} Options | ||
*/ | ||
|
||
const animationDuration = 0; | ||
|
||
/** @type {Options} */ | ||
let options = { | ||
title: () => 'Modal' | ||
} | ||
|
||
let isOpen = false | ||
|
||
/** | ||
* @param {Options} opts | ||
*/ | ||
export function openErrorModal(opts) { | ||
// Deep copy the supplied opts | ||
isOpen = true | ||
options = { ...opts } | ||
// Redraw by default unless caller suppressed | ||
if (options.redraw == null || options.redraw === true) { | ||
m.redraw() | ||
} | ||
} | ||
|
||
/** Calls redraw by default unless called with `false` */ | ||
export function closeErrorModal(redraw = true) { | ||
isOpen = false | ||
// Redraw by default unless caller suppressed | ||
if (redraw) { | ||
m.redraw() | ||
} | ||
} | ||
|
||
export function errorModalIsOpen() { | ||
return isOpen | ||
} | ||
|
||
let modelRef = undefined; | ||
|
||
/** Modal component */ | ||
export const ErrorModal = { | ||
oncreate() { | ||
const html = document.documentElement | ||
html.classList.add('error-modal-is-opening') | ||
setTimeout(() => { | ||
html.classList.remove('error-modal-is-opening') | ||
html.classList.add('error-modal-is-open') | ||
}, animationDuration) | ||
}, | ||
onbeforeremove() { | ||
const html = document.documentElement | ||
html.classList.remove('error-modal-is-open', 'error-modal-is-opening') | ||
html.classList.add('error-modal-is-closing') | ||
return new Promise(r => { | ||
setTimeout(r, animationDuration) | ||
}) | ||
}, | ||
onremove() { | ||
document.documentElement.classList.remove('error-modal-is-closing') | ||
}, | ||
view() { | ||
return m('dialog.error', { | ||
open: true, | ||
oncreate: ({ dom }) => { | ||
modelRef = dom; | ||
}, | ||
onclick: (e) => { | ||
e.redraw = false | ||
if (e.target === modelRef) { | ||
closeErrorModal() | ||
} | ||
} | ||
}, | ||
m('article', | ||
m('a.close', { | ||
href: '#', ariaLabel: 'Close', onclick: (e) => { | ||
isOpen = !isOpen | ||
e.preventDefault() | ||
options.onclose && options.onclose() | ||
} | ||
}, ''), | ||
options.title(), | ||
options.body != null && options.body(), | ||
) | ||
) | ||
} | ||
} |
Oops, something went wrong.