[ad_1]
Decentralization / Social media
The presentations page
In this part, we’ll add a page to list presentations for a debate. We pick up where we left off:
git clone https://github.com/jeremyorme/debate.git
cd debate
git checkout release/0.0.4
Presentations are a similar to messages and so the page is implemented similarly to the messages page. The key differences are:
- Only one presentation per user per debate — a user adds a presentation to summarize their side of the debate so it doesn’t make sense to have more than one. If a second presentation is added, it overwrites the first.
- A presentation must specify a URL — that points to the presentation video or document, otherwise it would just be a message.
- Presentations page provides voting buttons for and against — there is no presumption about which side the user intends to vote for on the presentations page as it displays presentations from both sides.
Let’s begin with the data
We need to define an interface that represents a presentation object in src/AppData.ts
:
export interface IPresentation extends IDbEntry {
title: string;
url: string;
}
Then we create a CollectionManager<IPresentation>
. Unlike for messages, where we had two collections for the two sides, we only have one for presentations:
// Presentationsprivate _presentations: CollectionManager<IPresentation> = new CollectionManager<IPresentation>();
Next, we add the familiar load method to open the collection:
async loadPresentations(debateId: string) {
if (!this._db)
return;const collectionName = 'debate-' + debateId + '-presentations';
this._presentations.init(await this._db.collection(collectionName, {
publicAccess: AccessRights.ReadAnyWriteOwn,
conflictResolution: ConflictResolution.LastWriteWins
}));
}
This opens a presentations collection for a given debateId
, allowing access for users to write their own key only (AccessRights.ReadAnyWriteOwn
) and to edit that key as many times as they like (ConflictResolution.LastWriteWins
).
We then add the usual methods to read and write the collection and listen for updates:
presentations(): IPresentation[] {
return this._presentations.entries() || [];
}onPresentations(callback: () => void) {
return this._presentations.onUpdated(callback);
}
addPresentation(presentation: IPresentation) {
if (this._publicKey)
this._presentations.addEntry({ ...presentation, _id: this._publicKey });
}
Note that addPresentation
sets _id
to the user’s public key (the only key in the collection that they are allowed to write).
The presentations page
The messages page and presentations page are very similar so we can start by creating copies of src/pages/MessagesPage.css
and src/pages/MessagesPage.tsx
and renaming them to src/pages/PresentationsPage.css
and src/pages/PresentationsPage.tsx
.
We then update instances of message
in src/pages/PresentationsPage.tsx
to be presentation
and remove occurrences of side
:
// ...
import "./PresentationsPage.css";// ...
interface ContainerParams {
id: string;
}
const PAGE_ID = 'presentations-page';
const PresentationsPage: React.FC<ContainerProps> = ({ appData }) => {
// ...
const [presentations, setPresentations] = useState(appData.presentations());
// ...
useEffect(() => {
appData.loadPresentations(id);
// ...
return appData.onDebatesUpdated(() => {
// ...
appData.loadPresentations(id);
// ...
});
}, []);
useEffect(() => {
return appData.onPresentations(() => {
setPresentations(appData.presentations());
});
}, []);
// ...
const addPresentation = () => {
const presentation: IPresentation = {
...dbEntryDefaults,
// ...
};
appData.addPresentation(presentation);
// ...
}
// ...
return (
<IonPage>
...
<IonTitle>{debateTitle}</IonTitle>
...
<IonContent>
{presentations.filter(p => p.url).map(p => <MessageCard key={p._id} username={p._identity.publicKey.slice(-8)} title={p.title} description="" url={p.url} />)}
</IonContent>
</IonPage>
);
};
export default PresentationsPage;
Now we can add our presentations page as a route in src/App.tsx
:
<Route path="/debate/:id/presentations">
<PresentationsPage appData={appData} />
</Route>
Then we can link to it from the third button on our DebateCard
component:
<IonItem>
<Link to={'/debate/' + id + '/presentations'}>
<IonIcon size="small" icon={videocamSharp} />
</Link>
<IonBadge className="count">11</IonBadge>
</IonItem>
Adding a presentation
We want to make sure a URL is provided when adding a presentation so we’ll render a separate input for the URL:
<IonCard>
<IonGrid>
<IonRow>
<IonCol>
<IonInput placeholder="Title" value={title} onIonChange={e => updateTitle(e.detail.value)} />
</IonCol>
</IonRow>
<IonRow>
<IonCol>
<IonInput placeholder="URL" value={url} onIonChange={e => updateUrl(e.detail.value)} />
</IonCol>
<IonCol size="auto">
<IonButton size="small" fill="clear" disabled={url.length == 0} onClick={() => addPresentation()}>
<IonIcon icon={addSharp} />
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</IonCard>
We need to replace the description
state variable with title
and url
state variables:
const [title, setTitle] = useState('');
const [url, setUrl] = useState('');
And replace the updateDescription
helper with similar helpers for title
and url
:
const updateTitle = (value: string | null | undefined) => {
if (!value && value != '')
return;setTitle(value);
};
const updateUrl = (value: string | null | undefined) => {
if (!value && value != '')
return;
setUrl(value);
};
Then we need to change addPresentation
to set title
and url
in the presentation
object and to reset them to empty string after adding the presentation:
const addPresentation = () => {
const presentation: IPresentation = {
...dbEntryDefaults,
title,
url
};
appData.addPresentation(presentation);
setTitle('');
setUrl('');
}
Voting buttons
The last thing we need to do is render both of the voting buttons:
<IonButtons slot="end">
<IonButton slot="icon-only" onClick={() => updateOwnVoteDirection(VoteDirection.For)}>
<IonIcon icon={ownVoteDirection == VoteDirection.For ? thumbsUpSharp : thumbsUpOutline} />
</IonButton>
<IonButton slot="icon-only" onClick={() => updateOwnVoteDirection(VoteDirection.Against)}>
<IonIcon icon={ownVoteDirection == VoteDirection.Against ? thumbsDownSharp : thumbsDownOutline} />
</IonButton>
</IonButtons>
Now we have a presentation page where users can post a summary video or document summing up their side of the debate from which users can vote for or against the debate.
You can get the full source to the end of this article from the 0.0.5 release branch:
git clone https://github.com/jeremyorme/debate.git
cd debate
git checkout release/0.0.5
Next time we’ll look at limiting a debate to start and stop at the designated times.
[ad_2]
Source link