Skip to content

Commit

Permalink
feat(forum): improve topic creation onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
Grafikart committed Nov 4, 2024
1 parent 5ad9dcd commit b912222
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 8 deletions.
56 changes: 56 additions & 0 deletions assets/css/pages/_forum.scss
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,59 @@
border-right: 10px solid #0000;
border-left: 10px solid #0000;
}

// Onboarding sur la création de sujet
// =============
.onboarding-step {
display: grid;
align-items: flex-start;
grid-template-columns: 32px 1fr;
gap: 1rem;
counter-increment: onboarding;
interpolate-size: allow-keywords;
}
.onboarding-step p {
color: var(--color-light);
}
.onboarding-step__number {
width: 32px;
height: 32px;
font-size: 1.2rem;
line-height: 1;
font-weight: bold;
border-radius: 50%;
display: grid;
place-items: center;
}
.onboarding-step__body {
height: auto;
transition: height .5s;
overflow: hidden;
}
.onboarding-step__number::after {
content: counter(onboarding);
}
.onboarding-step__number svg {
width: 32px;
height: 32px;
display: none;
}
.is-done .onboarding-step__number {
box-shadow: inset 0 0 0 3px var(--green);
background: var(--color);
color: var(--green);
border: none;
}
.is-done .onboarding-step__number svg {
display: block;
}
.is-done .onboarding-step__number::after {
display: none;
}
.is-done .onboarding-step__body,
.is-disabled .onboarding-step__body {
height: 32px;
}
.is-disabled.onboarding-step {
opacity: .3;
}
9 changes: 9 additions & 0 deletions assets/css/tools/_typography.scss
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ small, .text-small {
font-size: .9em;
}

.snippet {
font-size: .95rem;
border: solid 1px var(--border);
background-color: var(--list-hover);
display: inline-block;
border-radius: .25rem;
padding: .4rem .5rem;
}

// Pour générer les polices : https://transfonter.org/

// Alignements
Expand Down
5 changes: 3 additions & 2 deletions assets/elements/editor/Toolbar.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
* @property {HTMLDivElement} element
*/
import { BoldButton, FullScreenButton, LinkButton, SpeechButton } from './buttons'
import { BoldButton, FullScreenButton, LinkButton } from './buttons'
import { createElement } from '/functions/dom.js'
import CodeButton from '/elements/editor/buttons/CodeButton.js'

export default class Toolbar {
/**
Expand All @@ -14,7 +15,7 @@ export default class Toolbar {
const left = createElement('div', { class: 'mdeditor__toolbarleft' })
const right = createElement('div', { class: 'mdeditor__toolbarright' })
const fullScreenButton = new FullScreenButton(editor)
this.addButtons(left, [new BoldButton(editor), new LinkButton(editor), new SpeechButton(editor)])
this.addButtons(left, [new CodeButton(editor), new BoldButton(editor), new LinkButton(editor)])
this.addButtons(right, [fullScreenButton])
this.element.appendChild(left)
this.element.appendChild(right)
Expand Down
21 changes: 21 additions & 0 deletions assets/elements/editor/buttons/CodeButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Button from './Button'
import { strToDom } from '/functions/dom.js'

export default class CodeButton extends Button {
shortcut () {
return false
}

icon () {
return strToDom(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M23 12L15.9289 19.0711L14.5147 17.6569L20.1716 12L14.5147 6.34317L15.9289 4.92896L23 12ZM3.82843 12L9.48528 17.6569L8.07107 19.0711L1 12L8.07107 4.92896L9.48528 6.34317L3.82843 12Z"></path></svg>'
)
}

/**
* @param {Editor} editor
*/
action () {
this.editor.wrapWith('\n```\n', '\n```\n')
}
}
40 changes: 40 additions & 0 deletions assets/elements/forum/ForumOnboarding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { flash } from '/elements/Alert.js'

export class ForumOnboarding extends HTMLElement {
#step = 0

onSubmit = e => {
const form = e.currentTarget
if (this.#step > 1) {
return
}
e.preventDefault()
const answer = new FormData(form)
.get('challenge')
.split('\n')
.map(line => line.trim())
.filter(line => line !== '')
.join('\n')
if (
(this.#step === 0 && answer.includes('**vérifier**')) ||
(this.#step === 1 && answer.includes('```\nconst') && answer.includes(')\n```'))
) {
this.#step++
form.classList.add('is-done')
const nextForm = form.nextElementSibling
nextForm.classList.remove('is-disabled')
nextForm.querySelector('button[type="submit"]').removeAttribute('disabled')
} else {
flash(
this.#step === 0 ? 'Vous devez mettre le mot "vérifier" en gras' : 'Vous devez entourer le code de ```',
'danger'
)
}
}

connectedCallback () {
this.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', this.onSubmit)
})
}
}
2 changes: 2 additions & 0 deletions assets/elements/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { MarkAsWatched } from '/elements/courses/MarkAsWatched.jsx'
import { PodcastVote } from '/elements/PodcastVote.js'
import { SpoilerBox } from '/elements/SpoilerBox'
import {Captcha} from '/elements/Captcha'
import {ForumOnboarding} from "/elements/forum/ForumOnboarding.js";

// Custom Elements
customElements.define('nav-tabs', NavTabs)
Expand Down Expand Up @@ -64,6 +65,7 @@ customElements.define('premium-player', PremiumPlayer)
customElements.define('loader-overlay', LoaderOverlay)
customElements.define('theme-switcher', ThemeSwitcher)
customElements.define('podcast-vote', PodcastVote)
customElements.define('forum-onboarding', ForumOnboarding)
preactCustomElement('site-notifications', Notifications)
preactCustomElement('contact-form', ContactForm)
preactCustomElement('comments-area', Comments, ['target'])
Expand Down
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"devDependencies": {
"@preact/preset-vite": "^2.8.2",
"doctoc": "^2.0.1",
"eslint-config-preact": "^1.5.0",
"prettier": "^3.2.5",
"prettier-standard": "^16.4.1",
"sass": "^1.77.2",
Expand Down
18 changes: 18 additions & 0 deletions src/Domain/Forum/Repository/TopicRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use App\Domain\Forum\Entity\Topic;
use App\Infrastructure\Orm\AbstractRepository;
use App\Infrastructure\Spam\SpammableRepositoryTrait;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Persistence\ManagerRegistry;
Expand All @@ -21,6 +22,23 @@ public function __construct(ManagerRegistry $registry)
parent::__construct($registry, Topic::class);
}

public function hasTopics(User $user): bool
{
try {
$this->createQueryBuilder('t')
->select('t.id')
->where('t.author = :user')
->setMaxResults(1)
->setParameter('user', $user)
->getQuery()
->getSingleScalarResult();
} catch (NoResultException) {
return false;
}

return true;
}

/**
* Récupère les derniers sujets créés par l'utilisateur.
*
Expand Down
8 changes: 5 additions & 3 deletions src/Http/Controller/ForumController.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,17 @@ public function showLegacy(int $id): Response
}

#[Route(path: '/forum/new', name: 'forum_new')]
public function create(Request $request): Response
public function create(Request $request, TopicRepository $topicRepository): Response
{
$this->denyAccessUnlessGranted(ForumVoter::CREATE_TOPIC);
/** @var User $user */
$user = $this->getUser();
$topic = (new Topic())->setContent($this->renderView('forum/template/placeholder.text.twig'));
$topic = new Topic();
$topic->setAuthor($user);
$form = $this->createForm(ForumTopicForm::class, $topic);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$isSubmitted = $form->isSubmitted();
if ($isSubmitted && $form->isValid()) {
$this->topicService->createTopic($topic);
$this->addFlash('success', 'Le sujet a bien été créé');

Expand All @@ -91,6 +92,7 @@ public function create(Request $request): Response

return $this->render('forum/new.html.twig', [
'form' => $form->createView(),
'has_onboarding' => !$isSubmitted && !$topicRepository->hasTopics($user),
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function setMercureCookie(ResponseEvent $event): void
->withExpires($exp)
->withValue($this->tokenFactory->create($channels, null, [
// We need to set a date without ms to avoid float in "exp"
'exp' => new \DateTimeImmutable("@" . $exp->getTimestamp()),
'exp' => new \DateTimeImmutable('@'.$exp->getTimestamp()),
]));
$response->headers->setCookie($cookie);
}
Expand Down
2 changes: 0 additions & 2 deletions src/Infrastructure/Spam/GeoIpService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
namespace App\Infrastructure\Spam;

use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;
use MaxMind\Db\Reader\InvalidDatabaseException;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class GeoIpService
Expand Down
73 changes: 73 additions & 0 deletions templates/forum/new.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,78 @@

{% block body %}

{% if has_onboarding %}
<section class="container my5">

<h1 class="hero-title mb2">
Créer un <strong>sujet</strong>
</h1>

<div class="hero-text mb5">
Le forum utilise la syntaxe <a href="https://fr.wikipedia.org/wiki/Markdown#Formatage" target="_blank" rel="noopener noreferrer">markdown</a>, voici quelques étapes pour vous aider à formater votre sujet.
</div>

<forum-onboarding class="stack" style="--gap: 4">

<form class="onboarding-step">
<div class="onboarding-step__number card">{{ icon('check') }}</div>
<div class="onboarding-step__body stack">
<h2 class="h3 onboarding-step__title">Mettre en gras</h2>
<p class="text-big">
Pour mettre du texte en gras, vous pouvez l'entourer de deux * (exemple : <span class="snippet">ceci est **en gras**</span>)
</p>
<textarea name="challenge" is="markdown-editor" class="form-control" aria-label="Exercice pour la mise en gras en markdown">Pour vérifier que vous avez bien compris le formattage mettez le deuxième mot de cette phrase en gras.</textarea>
<button class="btn btn-secondary">Répondre</button>
</div>
</form>

<form class="onboarding-step is-disabled">
<div class="onboarding-step__number card">{{ icon('check') }}</div>
<div class="onboarding-step__body stack">
<h2 class="h3 onboarding-step__title">Formater le code</h2>
<p class="text-big">
Si votre question contient des morceaux de code, vous devez l'entourer de 3 `, exemple :
</p>
<pre class="snippet">J'ai un bug sur ce code

```
const a = 3
```</pre>
<p>
Pour vérifier que vous avez bien compris, entourez le code
</p>
<textarea name="challenge" is="markdown-editor" class="form-control" aria-label="Exercice pour la mise en gras en markdown">Ceci est un exemple de sujet, avec du code à l'intérieur. Mais le code n'est pas formatté correctement :(, entourez le code de 3 ` pour le mettre en forme.

const name = "John"
console.log("Bonjour " + name)
</textarea>
<button class="btn btn-secondary">Répondre</button>
</div>
</form>

<div class="onboarding-step is-disabled">
<div class="onboarding-step__number card">{{ icon('check') }}</div>
<div class="onboarding-step__body stack">
<h2 class="h3 onboarding-step__title">Créer votre sujet</h2>
<p class="text-big">
Afin de maximiser vos chances d'obtenir de l'aide, essayez de décrire au mieux votre problème (ne copiez pas des
centaines de lignes). Essayez de simplifier votre problème au maximum.
</p>
{{ form_start(form, {attr: {class: 'grid fit js-preventMultiSubmit', style: '--col:300px'}}) }}
{{ form_row(form.name, {label: 'Titre'}) }}
{{ form_row(form.tags, {label: 'Tags', attr: {placeholder: 'Sélectionner un ou plusieurs tags'}}) }}
{{ form_row(form.content, {label: false}) }}
<div class="full text-right">
<button type="submit" class="btn-primary" disabled>Créer le sujet</button>
</div>
{{ form_end(form) }}
</div>
</div>

</forum-onboarding>
</section>
{% else %}

<section class="container hero-grid my5">
<div class="stack" style="align-self: flex-start">
<h2 class="hero-title">
Expand All @@ -28,6 +100,7 @@
</div>
{{ form_end(form) }}
</div>
{% endif %}
</section>


Expand Down

0 comments on commit b912222

Please sign in to comment.