Providers with {reactData}
Four providers in _providers/ query the database, each building on the one before it; {reactData} writes every answer as a mount point and React draws the forum topic.
{reactData}_providers/db()
getAttribute('data')
Live
How it is built
PAD, on the server React, in the browser
{reactData id='topic', provider='topic', $id=1}
{reactData id='board', provider='board_from_topic'}
{reactData id='user', provider='user_from_topic'}
{reactData id='posts', provider='posts_from_topic'}
<div id="topic-display"></div>
<script type="text/babel" src="{$padRoot}react/examples/topic.js"></script>// The forum topic: four mount points written by {reactData} - topic, board, user, posts -
// each a <div id="..." data="JSON">. The attribute is a plain data, not a data-* one, so it
// is read with getAttribute('data'): dataset.data is undefined.
function readMount(id) {
return JSON.parse(document.getElementById(id).getAttribute('data'));
}
function formatWhen(text) {
return new Date(text.replace(' ', 'T')).toLocaleString('en', { dateStyle: 'medium', timeStyle: 'short' });
}
function TopicDisplay() {
const topic = readMount('topic');
const board = readMount('board');
const user = readMount('user');
const posts = readMount('posts');
const [open, setOpen] = React.useState(null);
const [liked, setLiked] = React.useState({});
return (
<div>
<div className="crumbs" style={{ marginBottom: 10 }}>
<span>๐ Forum</span><span>/</span><span>{board.name}</span>
</div>
<div className="card" style={{ borderTop: `4px solid ${topic.is_locked ? 'var(--bad)' : topic.is_pinned ? 'var(--warn)' : 'var(--pad)'}` }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<h2 style={{ margin: 0 }}>{topic.title}</h2>
<div className="chips">
{topic.is_pinned === 1 && <span className="badge">๐ pinned</span>}
{topic.is_locked === 1 && <span className="badge is-bad">๐ locked</span>}
</div>
</div>
<div className="chips" style={{ margin: '12px 0' }}>
<span className="badge is-pad">๐ค {user.username}{user.role === 'admin' ? ' ยท admin' : ''}</span>
<span className="badge">๐
{formatWhen(topic.created_at)}</span>
<span className="badge">๐ {topic.views} views</span>
</div>
<div className="panel">
<div className="panel-title">Board</div>
<strong>{board.name}</strong>
<div className="muted small">{board.description}</div>
</div>
</div>
<h3 style={{ margin: '24px 0 12px' }}>๐ฌ {posts.length} {posts.length === 1 ? 'post' : 'posts'}</h3>
{posts.length === 0 && <div className="empty-state"><span className="big">๐ญ</span>No posts yet.</div>}
<div style={{ display: 'grid', gap: 12 }}>
{posts.map((post, index) => (
<div key={post.id} className="card rise" style={{ animationDelay: `${index * 80}ms`, cursor: 'pointer' }}
onClick={() => setOpen(open === post.id ? null : post.id)}>
<div style={{ display: 'flex', gap: 14 }}>
<Avatar name={post.username} hue={(post.user_id * 67) % 360} />
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<div>
<strong>{post.username}</strong>{' '}
{/* user is the topic's author (the user_from_topic provider), not whoever
is looking - the page has no notion of a viewer - so the badge marks
the author's posts */}
{post.user_id === user.id && <span className="badge is-react">author</span>}
</div>
<span className="muted small">#{index + 1} · {formatWhen(post.created_at)}</span>
</div>
<p style={{ margin: '8px 0 0' }}>{post.content}</p>
{open === post.id && (
<div className="panel rise small" style={{ marginTop: 10 }}>
post {post.id} · user {post.user_id} · updated {formatWhen(post.updated_at)}
</div>
)}
</div>
<button className="btn-sm btn-ghost" style={{ alignSelf: 'start' }}
onClick={event => { event.stopPropagation(); setLiked({ ...liked, [post.id]: !liked[post.id] }); }}>
{liked[post.id] ? 'โค๏ธ' : '๐ค'}
</button>
</div>
</div>
))}
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('topic-display')).render(<TopicDisplay />);<?php
return db ("RECORD * FROM forum_topics WHERE id={0}", [$id] );
?><?php
return db ( "RECORD * FROM forum_boards WHERE id={0}",
[ $padProviders ['topic'] ['board_id'] ]
);
?><?php
// The topic's author as the component shows it - the name and the role. What a provider
// returns is in the page source for every visitor, so the e-mail address is left out:
// nothing showed it, and every visitor got it.
return db ( "RECORD id, username, role, created_at FROM users WHERE id={0}",
[ $padProviders ['topic'] ['user_id'] ] );
?><?php
return array_values (
db ( "ARRAY p.*, u.username
FROM forum_posts p
JOIN users u ON p.user_id = u.id
WHERE p.topic_id={0}
ORDER BY p.created_at", [ $padProviders ['topic'] ['id'] ] )
);
?>