diff --git a/package-lock.json b/package-lock.json
index db6b3447ef3420d528ca3246732c68f2713105ed..ae8c27a2b00569114e537719e3a120124ff84acb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11347,6 +11347,11 @@
       "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
       "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
     },
+    "react-mde": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/react-mde/-/react-mde-11.0.0.tgz",
+      "integrity": "sha512-U3k/ITPXklEjXkKhR7rgI3Y7ii5V62slSmG+/rYDQaCAabNwX+5dULKpIxWWSyqi+PvsuRVEYx6vV4sECMMbCw=="
+    },
     "react-modal": {
       "version": "3.12.1",
       "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.12.1.tgz",
@@ -12596,6 +12601,63 @@
       "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
       "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
     },
+    "showdown": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz",
+      "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==",
+      "requires": {
+        "yargs": "^14.2"
+      },
+      "dependencies": {
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "yargs": {
+          "version": "14.2.3",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+          "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+          "requires": {
+            "cliui": "^5.0.0",
+            "decamelize": "^1.2.0",
+            "find-up": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^3.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^15.0.1"
+          }
+        },
+        "yargs-parser": {
+          "version": "15.0.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+          "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
     "side-channel": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz",
diff --git a/package.json b/package.json
index 3b0975013e5815096adb5a70b33f59519b168ef4..fe1ec7035b92d15b03b5b46660db6d8b40fcbd27 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,11 @@
     "react-device-detect": "^1.13.1",
     "react-dom": "^16.13.1",
     "react-intersection-observer": "^8.31.0",
+    "react-mde": "^11.0.0",
     "react-modal": "^3.12.1",
     "react-router-dom": "^5.2.0",
     "react-scripts": "3.4.3",
+    "showdown": "^1.9.1",
     "unfetch": "^4.2.0",
     "wait-queue": "^1.1.4"
   },
diff --git a/src/actions/announcements.js b/src/actions/announcements.js
index 738ef521f3b6f448fa05c13dbffbd46ce1b0d3fd..cb99ea52220d8b0d8460ed11e041849f0ab2c7fa 100644
--- a/src/actions/announcements.js
+++ b/src/actions/announcements.js
@@ -45,7 +45,11 @@ export const addAnnouncement = createAsyncAction(
         link,
         type: announcementTypeMappingRev[type],
       });
-      const resp = await fetch("/announcements", { method: "POST", body });
+      const resp = await fetch("/announcements", {
+        method: "POST",
+        body,
+        expectedStatus: 201,
+      });
       const data = await resp.json();
       return successResult(data.data);
     } catch (err) {
@@ -64,7 +68,10 @@ export const deleteAnnouncement = createAsyncAction(
    */
   async (item) => {
     try {
-      await fetch(`/announcements/${item.id}`, { method: "DELETE" });
+      await fetch(`/announcements/${item.id}`, {
+        method: "DELETE",
+        expectedStatus: 204,
+      });
       return successResult({ item });
     } catch (err) {
       return errorResult([], err.toString());
@@ -86,7 +93,11 @@ export const updateAnnouncementContent = createAsyncAction(
       const body = JSON.stringify({
         content: newContent,
       });
-      await fetch(`/announcements/${item.id}`, { method: "PUT", body });
+      await fetch(`/announcements/${item.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult({ item, newContent });
     } catch (err) {
       return errorResult([], err.toString());
diff --git a/src/actions/posts.js b/src/actions/posts.js
index f2ca0a308d0785d0dd2eb103858a5a13b13ce20d..2e6d123a0692abb207466f051abb7b87bd3336ea 100644
--- a/src/actions/posts.js
+++ b/src/actions/posts.js
@@ -14,7 +14,7 @@ import {
 export const loadPosts = createAsyncAction(
   async () => {
     try {
-      const resp = await fetch("/posts");
+      const resp = await fetch("/posts", { expectedStatus: 200 });
       const data = await resp.json();
       return successResult(data.data);
     } catch (err) {
@@ -23,7 +23,7 @@ export const loadPosts = createAsyncAction(
   },
   {
     postActionHook: ({ result }) => {
-      if (!result.error) {
+      if (!result.error && result.payload) {
         const posts = result.payload.map(parseRawPost);
 
         PostStore.update((state) => {
@@ -49,7 +49,10 @@ export const like = createAsyncAction(
    */
   async (post) => {
     try {
-      await fetch(`/posts/${post.id}/like`, { method: "PATCH" });
+      await fetch(`/posts/${post.id}/like`, {
+        method: "PATCH",
+        expectedStatus: 204,
+      });
       return successResult(post);
     } catch (err) {
       return errorResult([], err.toString());
@@ -72,7 +75,10 @@ export const dislike = createAsyncAction(
    */
   async (post) => {
     try {
-      await fetch(`/posts/${post.id}/dislike`, { method: "PATCH" });
+      await fetch(`/posts/${post.id}/dislike`, {
+        method: "PATCH",
+        expectedStatus: 204,
+      });
       return successResult(post);
     } catch (err) {
       return errorResult([], err.toString());
@@ -98,7 +104,7 @@ export const addPost = createAsyncAction(async ({ content }) => {
       content,
       type: postsTypeMappingRev["post"],
     });
-    await fetch(`/posts`, { method: "POST", body });
+    await fetch(`/posts`, { method: "POST", body, expectedStatus: 201 });
     return successResult();
   } catch (err) {
     return errorResult([], err.toString());
@@ -114,7 +120,7 @@ export const addProposal = createAsyncAction(async ({ content }) => {
       content,
       type: postsTypeMappingRev["procedure-proposal"],
     });
-    await fetch(`/posts`, { method: "POST", body });
+    await fetch(`/posts`, { method: "POST", body, expectedStatus: 201 });
     return successResult();
   } catch (err) {
     return errorResult([], err.toString());
@@ -154,7 +160,35 @@ export const edit = createAsyncAction(
       const body = JSON.stringify({
         content: newContent,
       });
-      await fetch(`/posts/${post.id}`, { method: "PUT", body });
+      await fetch(`/posts/${post.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
+      return successResult();
+    } catch (err) {
+      return errorResult([], err.toString());
+    }
+  }
+);
+
+/**
+ * Archive post.
+ */
+export const archive = createAsyncAction(
+  /**
+   * @param {CF2021.Post} post
+   */
+  async (post) => {
+    try {
+      const body = JSON.stringify({
+        is_archived: true,
+      });
+      await fetch(`/posts/${post.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult();
     } catch (err) {
       return errorResult([], err.toString());
@@ -171,7 +205,11 @@ const updateProposalState = async (proposal, state) => {
   const body = JSON.stringify({
     state: postsStateMappingRev[state],
   });
-  await fetch(`/posts/${proposal.id}`, { method: "PUT", body });
+  await fetch(`/posts/${proposal.id}`, {
+    method: "PUT",
+    body,
+    expectedStatus: 204,
+  });
   return successResult(proposal);
 };
 
diff --git a/src/actions/program.js b/src/actions/program.js
index be1612589476f7c3da615978fdd04e14acf9bd42..56a3f18073c62c48eef338cb6590a1d40b42e275 100644
--- a/src/actions/program.js
+++ b/src/actions/program.js
@@ -71,7 +71,11 @@ export const renameProgramPoint = createAsyncAction(
       const body = JSON.stringify({
         title: newTitle,
       });
-      await fetch(`/program/${programEntry.id}`, { method: "PUT", body });
+      await fetch(`/program/${programEntry.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult({ programEntry, newTitle });
     } catch (err) {
       return errorResult([], err.toString());
@@ -104,7 +108,11 @@ export const endProgramPoint = createAsyncAction(
       const body = JSON.stringify({
         is_live: false,
       });
-      await fetch(`/program/${programEntry.id}`, { method: "PUT", body });
+      await fetch(`/program/${programEntry.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult(programEntry);
     } catch (err) {
       return errorResult([], err.toString());
@@ -134,7 +142,11 @@ export const activateProgramPoint = createAsyncAction(
       const body = JSON.stringify({
         is_live: true,
       });
-      await fetch(`/program/${programEntry.id}`, { method: "PUT", body });
+      await fetch(`/program/${programEntry.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult(programEntry);
     } catch (err) {
       return errorResult([], err.toString());
@@ -167,7 +179,11 @@ export const openDiscussion = createAsyncAction(
       const body = JSON.stringify({
         discussion_opened: true,
       });
-      await fetch(`/program/${programEntry.id}`, { method: "PUT", body });
+      await fetch(`/program/${programEntry.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult(programEntry);
     } catch (err) {
       return errorResult([], err.toString());
@@ -195,7 +211,11 @@ export const closeDiscussion = createAsyncAction(
       const body = JSON.stringify({
         discussion_opened: false,
       });
-      await fetch(`/program/${programEntry.id}`, { method: "PUT", body });
+      await fetch(`/program/${programEntry.id}`, {
+        method: "PUT",
+        body,
+        expectedStatus: 204,
+      });
       return successResult(programEntry);
     } catch (err) {
       return errorResult([], err.toString());
diff --git a/src/api.js b/src/api.js
index c7d5efd34840b6f275fe913ab5c692ccf527a548..8bfc444b5d16023621fa24725168fe293fbc7628 100644
--- a/src/api.js
+++ b/src/api.js
@@ -2,19 +2,29 @@ import baseFetch from "unfetch";
 
 import { AuthStore } from "./stores";
 
-export const fetch = (url, opts) => {
+export const fetch = async (
+  url,
+  { headers = {}, expectedStatus = 200, method = "GET", body = null } = {}
+) => {
   const { isAuthenticated, user } = AuthStore.getRawState();
 
-  opts = opts || {};
-  opts.headers = opts.headers || {};
-
   if (isAuthenticated) {
-    opts.headers.Authorization = "Bearer " + user.accessToken;
+    headers.Authorization = "Bearer " + user.accessToken;
+  }
+
+  if (!headers["Content-Type"]) {
+    headers["Content-Type"] = "application/json";
   }
 
-  if (!opts.headers["Content-Type"]) {
-    opts.headers["Content-Type"] = "application/json";
+  const response = await baseFetch(process.env.REACT_APP_API_BASE_URL + url, {
+    body,
+    method,
+    headers,
+  });
+
+  if (!!expectedStatus && response.status !== expectedStatus) {
+    throw new Error(`Unexpected status code ${response.status}`);
   }
 
-  return baseFetch(process.env.REACT_APP_API_BASE_URL + url, opts);
+  return response;
 };
diff --git a/src/components/MarkdownEditor.jsx b/src/components/MarkdownEditor.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..1379fe5a176ad1317c57299f502a7ab0c1bd708a
--- /dev/null
+++ b/src/components/MarkdownEditor.jsx
@@ -0,0 +1,26 @@
+import React from "react";
+import ReactMde from "react-mde";
+import Showdown from "showdown";
+
+const converter = new Showdown.Converter({
+  tables: true,
+  simplifiedAutoLink: true,
+  strikethrough: true,
+  tasklists: true,
+});
+
+const MarkdownEditor = ({ value, onChange }) => {
+  return (
+    <ReactMde
+      value={value}
+      onChange={onChange}
+      // selectedTab={selectedTab}
+      // onTabChange={setSelectedTab}
+      generateMarkdownPreview={(markdown) =>
+        Promise.resolve(converter.makeHtml(markdown))
+      }
+    />
+  );
+};
+
+export default MarkdownEditor;
diff --git a/src/components/annoucements/AnnouncementEditModal.jsx b/src/components/annoucements/AnnouncementEditModal.jsx
index df5d583f03c86f6f4c33377138355f9a5a36f281..c0bed97aeb03f8044cdaa4ea0e78dc39de345527 100644
--- a/src/components/annoucements/AnnouncementEditModal.jsx
+++ b/src/components/annoucements/AnnouncementEditModal.jsx
@@ -2,12 +2,15 @@ import React, { useState } from "react";
 
 import Button from "components/Button";
 import { Card, CardActions, CardBody, CardHeadline } from "components/cards";
+import ErrorMessage from "components/ErrorMessage";
 import Modal from "components/modals/Modal";
 
 const AnnouncementEditModal = ({
   announcement,
   onCancel,
   onConfirm,
+  confirming,
+  error,
   ...props
 }) => {
   const [text, setText] = useState(announcement.content);
@@ -46,12 +49,18 @@ const AnnouncementEditModal = ({
               ></textarea>
             </div>
           </div>
+          {error && (
+            <ErrorMessage className="mt-2">
+              Při editaci došlo k problému: {error}
+            </ErrorMessage>
+          )}
         </CardBody>
         <CardActions right className="space-x-1">
           <Button
             hoverActive
             color="blue-300"
             className="text-sm"
+            loading={confirming}
             onClick={confirm}
           >
             Uložit
diff --git a/src/components/modals/ModalConfirm.jsx b/src/components/modals/ModalConfirm.jsx
index f0cfe1e8c6e6e6b150a6011d6b707f76cb8db89d..8efbbeca96411f6b520072813b5e1eaf8d936be1 100644
--- a/src/components/modals/ModalConfirm.jsx
+++ b/src/components/modals/ModalConfirm.jsx
@@ -36,7 +36,7 @@ const ModalConfirm = ({
           <CardBodyText>{children}</CardBodyText>
           {error && (
             <ErrorMessage className="mt-2">
-              Při provádění akce došlo k problému: error
+              Při provádění akce došlo k problému: {error}
             </ErrorMessage>
           )}
         </CardBody>
@@ -64,4 +64,4 @@ const ModalConfirm = ({
   );
 };
 
-export default ModalConfirm;
+export default React.memo(ModalConfirm);
diff --git a/src/components/posts/Post.jsx b/src/components/posts/Post.jsx
index cd67cbf999a40e781da354b101b85165c7934223..72ed9925703251ea0ab18032730d8a93f37db9a6 100644
--- a/src/components/posts/Post.jsx
+++ b/src/components/posts/Post.jsx
@@ -30,6 +30,7 @@ const Post = ({
   onRejectProcedureProposal,
   onRejectProcedureProposalByChairman,
   onEdit,
+  onArchive,
   onSeen,
 }) => {
   const { ref, inView } = useInView({
@@ -132,6 +133,7 @@ const Post = ({
   const showEditAction = true;
   const showBanAction = true;
   const showHideAction = !archived;
+  const showArchiveAction = !archived;
 
   return (
     <div className={wrapperClassName} ref={ref}>
@@ -222,6 +224,13 @@ const Post = ({
                       title="Skrýt příspěvek"
                     />
                   )}
+                  {showArchiveAction && (
+                    <DropdownMenuItem
+                      onClick={onArchive}
+                      icon="ico--drawer"
+                      title="Archivovat příspěvek"
+                    />
+                  )}
                 </DropdownMenu>
               )}
             </div>
diff --git a/src/components/posts/PostEditModal.jsx b/src/components/posts/PostEditModal.jsx
index b801099744e01f08b6de40275ce1b012ffd4b3dc..72b5c0c3cdc09da0ff1c415857196c87f75977a1 100644
--- a/src/components/posts/PostEditModal.jsx
+++ b/src/components/posts/PostEditModal.jsx
@@ -2,9 +2,17 @@ import React, { useState } from "react";
 
 import Button from "components/Button";
 import { Card, CardActions, CardBody, CardHeadline } from "components/cards";
+import ErrorMessage from "components/ErrorMessage";
 import Modal from "components/modals/Modal";
 
-const PostEditModal = ({ post, onCancel, onConfirm, ...props }) => {
+const PostEditModal = ({
+  post,
+  onCancel,
+  onConfirm,
+  confirming,
+  error,
+  ...props
+}) => {
   const [text, setText] = useState(post.content);
 
   const onTextInput = (evt) => {
@@ -41,12 +49,18 @@ const PostEditModal = ({ post, onCancel, onConfirm, ...props }) => {
               ></textarea>
             </div>
           </div>
+          {error && (
+            <ErrorMessage className="mt-2">
+              Při editaci došlo k problému: {error}
+            </ErrorMessage>
+          )}
         </CardBody>
         <CardActions right className="space-x-1">
           <Button
             hoverActive
             color="blue-300"
             className="text-sm"
+            loading={confirming}
             onClick={confirm}
           >
             Uložit
diff --git a/src/components/posts/PostList.jsx b/src/components/posts/PostList.jsx
index 6faf9a2750c77a6481bb501a95c5b282b4e2c977..c8143ece2ba6ffa9a911990b0ab0289ee7f93482 100644
--- a/src/components/posts/PostList.jsx
+++ b/src/components/posts/PostList.jsx
@@ -17,6 +17,7 @@ const PostList = ({
   onRejectProcedureProposal,
   onRejectProcedureProposalByChairman,
   onEdit,
+  onArchive,
   onSeen,
   dimArchived,
 }) => {
@@ -30,6 +31,7 @@ const PostList = ({
   const onPostEdit = buildHandler(onEdit);
   const onPostHide = buildHandler(onHide);
   const onPostBanUser = buildHandler(onBanUser);
+  const onPostArchive = buildHandler(onArchive);
   const onPostAnnounceProcedureProposal = buildHandler(
     onAnnounceProcedureProposal
   );
@@ -74,6 +76,7 @@ const PostList = ({
               item
             )}
             onEdit={onPostEdit(item)}
+            onArchive={onPostArchive(item)}
             onSeen={onPostSeen(item)}
           />
         ))}
diff --git a/src/containers/AddAnnouncementForm.jsx b/src/containers/AddAnnouncementForm.jsx
index fdc732ecf6dae2a446eb04a0f0a766bc33507242..b83dfdd9948c2ec8a8c60a3c340e21b036091f0e 100644
--- a/src/containers/AddAnnouncementForm.jsx
+++ b/src/containers/AddAnnouncementForm.jsx
@@ -11,7 +11,8 @@ const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]
 const AddAnnouncementForm = ({ className }) => {
   const [text, setText] = useState("");
   const [link, setLink] = useState("");
-  const [linkValid, setLinkValid] = useState(false);
+  const [linkValid, setLinkValid] = useState(null);
+  const [noTextError, setNoTextError] = useState(false);
   const [type, setType] = useState("announcement");
 
   const [adding, addingError] = useActionState(addAnnouncement, {
@@ -22,24 +23,36 @@ const AddAnnouncementForm = ({ className }) => {
 
   const onTextInput = (evt) => {
     setText(evt.target.value);
+
+    if (evt.target.value !== "") {
+      setNoTextError(false);
+    }
   };
 
   const onLinkInput = (evt) => {
     setLink(evt.target.value);
 
     if (!!evt.target.value) {
-      setLinkValid(evt.target.value.match(urlRegex));
+      setLinkValid(!!evt.target.value.match(urlRegex));
     }
   };
 
   const onAdd = async (evt) => {
+    if (!link) {
+      setLinkValid(false);
+    }
+
     if (!!text) {
-      const result = await addAnnouncement.run({ content: text, link, type });
+      if (type === "voting" && link) {
+        const result = await addAnnouncement.run({ content: text, link, type });
 
-      if (!result.error) {
-        setText("");
-        setLink("");
+        if (!result.error) {
+          setText("");
+          setLink("");
+        }
       }
+    } else {
+      setNoTextError(true);
     }
   };
 
@@ -77,7 +90,11 @@ const AddAnnouncementForm = ({ className }) => {
           </div>
         </div>
 
-        <div className="form-field">
+        <div
+          className={classNames("form-field", {
+            "form-field--error": noTextError,
+          })}
+        >
           <div className="form-field__wrapper form-field__wrapper--shadowed">
             <textarea
               className="text-input text-sm form-field__control "
@@ -88,12 +105,17 @@ const AddAnnouncementForm = ({ className }) => {
               onChange={onTextInput}
             ></textarea>
           </div>
+          {noTextError && (
+            <div className="form-field__error">
+              Před přidáním oznámení nezapomeňte vyplnit jeho obsah.
+            </div>
+          )}
         </div>
 
         <div
           className={classNames("form-field", {
             hidden: type !== "voting",
-            "form-field--error": !!link && !linkValid,
+            "form-field--error": linkValid === false,
           })}
         >
           <div className="form-field__wrapper form-field__wrapper--shadowed">
@@ -108,7 +130,7 @@ const AddAnnouncementForm = ({ className }) => {
               <i className="ico--link1"></i>
             </div>
           </div>
-          {!!link && !linkValid && (
+          {linkValid === false && (
             <div className="form-field__error">Zadejte platnou URL.</div>
           )}
         </div>
@@ -119,7 +141,7 @@ const AddAnnouncementForm = ({ className }) => {
         className="text-sm mt-4"
         hoverActive
         loading={adding}
-        disabled={!text || (type === "voting" && !linkValid) || adding}
+        disabled={adding}
       >
         Přidat oznámení
       </Button>
diff --git a/src/containers/AddPostForm.jsx b/src/containers/AddPostForm.jsx
index dc2d90b6acd0f569323e8af8b2a9ab2e38ed290c..667278372f7f3d9b2292f89f66097261c54c4b77 100644
--- a/src/containers/AddPostForm.jsx
+++ b/src/containers/AddPostForm.jsx
@@ -1,4 +1,5 @@
 import React, { useState } from "react";
+import classNames from "classnames";
 
 import { addPost, addProposal } from "actions/posts";
 import Button from "components/Button";
@@ -7,6 +8,8 @@ import { useActionState } from "hooks";
 
 const AddPostForm = ({ className }) => {
   const [text, setText] = useState("");
+  const [type, setType] = useState("post");
+  const [noTextError, setNoTextError] = useState(false);
   const [addingPost, addingPostError] = useActionState(addPost, {
     content: text,
   });
@@ -16,37 +19,55 @@ const AddPostForm = ({ className }) => {
 
   const onTextInput = (evt) => {
     setText(evt.target.value);
+
+    if (evt.target.value !== "") {
+      setNoTextError(false);
+    }
   };
 
-  const onAddPost = async (evt) => {
+  const onAdd = async (evt) => {
     if (!!text) {
-      const result = await addPost.run({ content: text });
+      const result = await (type === "post" ? addPost : addProposal).run({
+        content: text,
+      });
 
       if (!result.error) {
         setText("");
       }
+    } else {
+      setNoTextError(true);
     }
   };
 
-  const onAddProposal = async (evt) => {
+  const setTypePost = (evt) => {
+    evt.preventDefault();
     evt.stopPropagation();
-
-    if (!!text) {
-      const result = await addProposal.run({ content: text });
-
-      if (!result.error) {
-        setText("");
-      }
-    }
+    setType("post");
+  };
+  const setTypeProposal = (evt) => {
+    evt.preventDefault();
+    evt.stopPropagation();
+    setType("procedure-proposal");
   };
 
   const buttonDropdownActionList = (
     <ul className="dropdown-button__choices bg-white text-black whitespace-no-wrap">
-      <li className="dropdown-button__choice hover:bg-grey-125">
-        <span className="block px-4 py-3" onClick={onAddProposal}>
-          Navrhnout postup
-        </span>
-      </li>
+      {type === "post" && (
+        <li
+          className="dropdown-button__choice hover:bg-grey-125"
+          onClick={setTypeProposal}
+        >
+          <span className="block px-4 py-3">Navrhnout postup</span>
+        </li>
+      )}
+      {type === "procedure-proposal" && (
+        <li
+          className="dropdown-button__choice hover:bg-grey-125"
+          onClick={setTypePost}
+        >
+          <span className="block px-4 py-3">Přidat příspěvek</span>
+        </li>
+      )}
     </ul>
   );
 
@@ -62,7 +83,11 @@ const AddPostForm = ({ className }) => {
           Při přidávání příspěvku došlo k problému: {addingProposalError}.
         </ErrorMessage>
       )}
-      <div className="form-field">
+      <div
+        className={classNames("form-field", {
+          "form-field--error": noTextError,
+        })}
+      >
         <div className="form-field__wrapper form-field__wrapper--shadowed">
           <textarea
             className="text-input form-field__control "
@@ -73,19 +98,25 @@ const AddPostForm = ({ className }) => {
             onChange={onTextInput}
           ></textarea>
         </div>
+        {noTextError && (
+          <div className="form-field__error">
+            Před přidáním příspěvku nezapomeňte vyplnit jeho obsah.
+          </div>
+        )}
       </div>
 
       <div className="space-x-4">
         <Button
-          onClick={onAddPost}
-          disabled={!text || addingPost || addingProposal}
+          onClick={onAdd}
+          disabled={addingPost || addingProposal}
           loading={addingPost || addingProposal}
           hoverActive
           icon="ico--chevron-down"
           iconWrapperClassName="dropdown-button"
           iconChildren={buttonDropdownActionList}
         >
-          Přidat příspěvek
+          {type === "post" && "Přidat příspěvek"}
+          {type === "procedure-proposal" && "Navrhnout postup"}
         </Button>
 
         <span className="text-sm text-grey-200 hidden lg:inline">
diff --git a/src/containers/AnnoucementsContainer.jsx b/src/containers/AnnoucementsContainer.jsx
index e8d6c04fe61cd8532f0e07eba521e1e6b5874bd2..cd8da9c91daf71ef321b31b5ad6761d5aa8fa692 100644
--- a/src/containers/AnnoucementsContainer.jsx
+++ b/src/containers/AnnoucementsContainer.jsx
@@ -15,6 +15,8 @@ import { AnnouncementStore, AuthStore } from "stores";
 
 const AnnoucementsContainer = () => {
   const [itemToEdit, setItemToEdit] = useState(null);
+  const [confirmingEdit, setConfirmingEdit] = useState(false);
+  const [editError, setEditError] = useState(null);
   const { 2: loadResult } = loadAnnouncements.useWatch();
 
   const [
@@ -37,8 +39,21 @@ const AnnoucementsContainer = () => {
   const confirmEdit = useCallback(
     async (newContent) => {
       if (itemToEdit && newContent) {
-        await updateAnnouncementContent.run({ item: itemToEdit, newContent });
-        setItemToEdit(null);
+        setConfirmingEdit(true);
+
+        const result = await updateAnnouncementContent.run({
+          item: itemToEdit,
+          newContent,
+        });
+
+        if (!result.error) {
+          setItemToEdit(null);
+          setEditError(null);
+        } else {
+          setEditError(result.message);
+        }
+
+        setConfirmingEdit(false);
       }
     },
     [itemToEdit, setItemToEdit]
@@ -91,6 +106,8 @@ const AnnoucementsContainer = () => {
           announcement={itemToEdit}
           onConfirm={confirmEdit}
           onCancel={cancelEdit}
+          confirming={confirmingEdit}
+          error={editError}
         />
       )}
     </>
diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx
index 936a3c6137aec03b21999f5682ae0c613baf9ddb..0efdc0c551a718013579cedbddf6daff544357df 100644
--- a/src/containers/PostsContainer.jsx
+++ b/src/containers/PostsContainer.jsx
@@ -4,6 +4,7 @@ import pick from "lodash/pick";
 import {
   acceptProposal,
   announceProposal,
+  archive,
   dislike,
   edit,
   hide,
@@ -22,6 +23,8 @@ import { AuthStore, PostStore } from "stores";
 
 const PostsContainer = ({ className }) => {
   const [postToEdit, setPostToEdit] = useState(null);
+  const [confirmingEdit, setConfirmingEdit] = useState(false);
+  const [editError, setEditError] = useState(null);
 
   const [
     userToBan,
@@ -35,6 +38,12 @@ const PostsContainer = ({ className }) => {
     onPostHideConfirm,
     onPostHideCancel,
   ] = useItemActionConfirm(hide);
+  const [
+    postToArchive,
+    setPostToArchive,
+    onPostArchiveConfirm,
+    onPostArchiveCancel,
+  ] = useItemActionConfirm(archive);
   const [
     postToAnnounce,
     setPostToAnnounce,
@@ -70,6 +79,10 @@ const PostsContainer = ({ className }) => {
 
   const [banningUser, banningUserError] = useActionState(ban, userToBan);
   const [hidingPost, hidingPostError] = useActionState(hide, postToHide);
+  const [archivingPost, archivingPostError] = useActionState(
+    archive,
+    postToArchive
+  );
   const [announcingProposal, announcingProposalError] = useActionState(
     announceProposal,
     postToAnnounce
@@ -92,8 +105,18 @@ const PostsContainer = ({ className }) => {
   const confirmEdit = useCallback(
     async (newContent) => {
       if (postToEdit && newContent) {
-        await edit.run({ post: postToEdit, newContent });
-        setPostToEdit(null);
+        setConfirmingEdit(true);
+
+        const result = await edit.run({ post: postToEdit, newContent });
+
+        if (!result.error) {
+          setPostToEdit(null);
+          setEditError(null);
+        } else {
+          setEditError(result.message);
+        }
+
+        setConfirmingEdit(false);
       }
     },
     [postToEdit, setPostToEdit]
@@ -145,6 +168,7 @@ const PostsContainer = ({ className }) => {
         onHide={setPostToHide}
         onBanUser={onBanUser}
         onEdit={setPostToEdit}
+        onArchive={setPostToArchive}
         onAnnounceProcedureProposal={setPostToAnnounce}
         onAcceptProcedureProposal={setPostToAccept}
         onRejectProcedureProposal={setPostToReject}
@@ -173,6 +197,18 @@ const PostsContainer = ({ className }) => {
       >
         Příspěvek se skryje a uživatelé ho neuvidí. Opravdu to chcete?
       </ModalConfirm>
+      <ModalConfirm
+        isOpen={!!postToArchive}
+        onConfirm={onPostArchiveConfirm}
+        onCancel={onPostArchiveCancel}
+        confirming={archivingPost}
+        error={archivingPostError}
+        title="Archivovat příspěvek?"
+        yesActionLabel="Potvrdit"
+      >
+        Příspěvek bude archivován a bude ve výpisu vizuálně odlišen. Opravdu to
+        chcete?
+      </ModalConfirm>
       <ModalConfirm
         isOpen={!!postToAnnounce}
         onConfirm={onAnnounceConfirm}
@@ -224,6 +260,8 @@ const PostsContainer = ({ className }) => {
           post={postToEdit}
           onConfirm={confirmEdit}
           onCancel={cancelEdit}
+          confirming={confirmingEdit}
+          error={editError}
         />
       )}
     </>
diff --git a/src/ws/handlers/posts.js b/src/ws/handlers/posts.js
index a277ed8a0843072a0407132aa444614dbf49df97..3c07ed0677243efddaa841762afffaf82d9c8fcd 100644
--- a/src/ws/handlers/posts.js
+++ b/src/ws/handlers/posts.js
@@ -30,6 +30,10 @@ export const handlePostChanged = (payload) => {
       if (has(payload, "state")) {
         state.items[payload.id].state = postsStateMapping[payload.state];
       }
+
+      if (has(payload, "is_archived")) {
+        state.items[payload.id].archived = payload.is_archived;
+      }
     }
   });
 };