diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml
index 4daf0aae..5e4f2fc5 100644
--- a/.github/workflows/docker-pr.yml
+++ b/.github/workflows/docker-pr.yml
@@ -15,7 +15,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3.0.2
- name: Build Docker image
- uses: docker/build-push-action@v3.0.0
+ uses: docker/build-push-action@v3.1.0
with:
context: .
push: false
diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml
index ee80ea85..857f9363 100644
--- a/.github/workflows/prod-deploy.yml
+++ b/.github/workflows/prod-deploy.yml
@@ -86,7 +86,7 @@ jobs:
with:
images: ajbura/cinny
- name: Build and push Docker image
- uses: docker/build-push-action@v3.0.0
+ uses: docker/build-push-action@v3.1.0
with:
context: .
platforms: linux/amd64,linux/arm64
diff --git a/Dockerfile b/Dockerfile
index a1066da1..ea1b96f2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@ RUN npm run build
## App
-FROM nginx:1.23.0-alpine
+FROM nginx:1.23.1-alpine
COPY --from=builder /src/dist /app
diff --git a/package-lock.json b/package-lock.json
index 827290fe..f258f7d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3076,6 +3076,8 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
+ "optional": true,
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -3091,7 +3093,9 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true
+ "dev": true,
+ "optional": true,
+ "peer": true
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
@@ -16963,15 +16967,14 @@
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
- "requires": {
- "ajv": "^8.0.0"
- },
+ "requires": {},
"dependencies": {
"ajv": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
+ "version": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
+ "optional": true,
+ "peer": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -16983,7 +16986,9 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true
+ "dev": true,
+ "optional": true,
+ "peer": true
}
}
},
diff --git a/public/res/ic/outlined/sticker.svg b/public/res/ic/outlined/sticker.svg
new file mode 100644
index 00000000..bc486e5e
--- /dev/null
+++ b/public/res/ic/outlined/sticker.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/app/atoms/button/Button.scss b/src/app/atoms/button/Button.scss
index 7b12195c..e1a01bb0 100644
--- a/src/app/atoms/button/Button.scss
+++ b/src/app/atoms/button/Button.scss
@@ -26,10 +26,10 @@
&--icon {
@include dir.side(padding, var(--sp-tight), var(--sp-loose));
- .ic-raw {
- @include dir.side(margin, 0, var(--sp-extra-tight));
- flex-shrink: 0;
- }
+ }
+ .ic-raw {
+ @include dir.side(margin, 0, var(--sp-extra-tight));
+ flex-shrink: 0;
}
}
diff --git a/src/app/atoms/math/Math.jsx b/src/app/atoms/math/Math.jsx
index dcfd0212..87f85899 100644
--- a/src/app/atoms/math/Math.jsx
+++ b/src/app/atoms/math/Math.jsx
@@ -5,7 +5,6 @@ import katex from 'katex';
import 'katex/dist/katex.min.css';
import 'katex/dist/contrib/copy-tex';
-import 'katex/dist/contrib/copy-tex.css';
const Math = React.memo(({
content, throwOnError, errorColor, displayMode,
diff --git a/src/app/atoms/time/Time.jsx b/src/app/atoms/time/Time.jsx
new file mode 100644
index 00000000..750b958f
--- /dev/null
+++ b/src/app/atoms/time/Time.jsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import dateFormat from 'dateformat';
+import { isInSameDay } from '../../../util/common';
+
+function Time({ timestamp, fullTime }) {
+ const date = new Date(timestamp);
+
+ const formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT');
+ let formattedDate = formattedFullTime;
+
+ if (!fullTime) {
+ const compareDate = new Date();
+ const isToday = isInSameDay(date, compareDate);
+ compareDate.setDate(compareDate.getDate() - 1);
+ const isYesterday = isInSameDay(date, compareDate);
+
+ formattedDate = dateFormat(date, isToday || isYesterday ? 'hh:MM TT' : 'dd/mm/yyyy');
+ if (isYesterday) {
+ formattedDate = `Yesterday, ${formattedDate}`;
+ }
+ }
+
+ return (
+
+ );
+}
+
+Time.defaultProps = {
+ fullTime: false,
+};
+
+Time.propTypes = {
+ timestamp: PropTypes.number.isRequired,
+ fullTime: PropTypes.bool,
+};
+
+export default Time;
diff --git a/src/app/molecules/image-pack/ImagePack.jsx b/src/app/molecules/image-pack/ImagePack.jsx
new file mode 100644
index 00000000..725291d1
--- /dev/null
+++ b/src/app/molecules/image-pack/ImagePack.jsx
@@ -0,0 +1,469 @@
+import React, {
+ useState, useMemo, useReducer, useEffect,
+} from 'react';
+import PropTypes from 'prop-types';
+import './ImagePack.scss';
+
+import initMatrix from '../../../client/initMatrix';
+import { openReusableDialog } from '../../../client/action/navigation';
+import { suffixRename } from '../../../util/common';
+
+import Button from '../../atoms/button/Button';
+import Text from '../../atoms/text/Text';
+import Input from '../../atoms/input/Input';
+import Checkbox from '../../atoms/button/Checkbox';
+import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
+
+import { ImagePack as ImagePackBuilder } from '../../organisms/emoji-board/custom-emoji';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+import ImagePackProfile from './ImagePackProfile';
+import ImagePackItem from './ImagePackItem';
+import ImagePackUpload from './ImagePackUpload';
+
+const renameImagePackItem = (shortcode) => new Promise((resolve) => {
+ let isCompleted = false;
+
+ openReusableDialog(
+