diff --git a/src/components/_shared/CheckButton/CheckButton.module.scss b/src/components/_shared/CheckButton/CheckButton.module.scss
index d62742f1..f7d6fd94 100644
--- a/src/components/_shared/CheckButton/CheckButton.module.scss
+++ b/src/components/_shared/CheckButton/CheckButton.module.scss
@@ -21,9 +21,11 @@
&:hover {
background: var(--background-color-invert);
color: var(--default-color-invert);
+
.check {
display: none;
}
+
.close {
display: block;
}
diff --git a/src/components/_shared/DropArea/DropArea.tsx b/src/components/_shared/DropArea/DropArea.tsx
index 2430d46b..8b0ca6e6 100644
--- a/src/components/_shared/DropArea/DropArea.tsx
+++ b/src/components/_shared/DropArea/DropArea.tsx
@@ -7,6 +7,7 @@ import { validateFiles } from '../../../utils/validateFile'
import type { FileTypeToUpload } from '../../../pages/types'
import { handleFileUpload } from '../../../utils/handleFileUpload'
import { UploadedFile } from '../../../pages/types'
+import { handleImageUpload } from '../../../utils/handleImageUpload'
type Props = {
class?: string
@@ -30,7 +31,8 @@ export const DropArea = (props: Props) => {
const results: UploadedFile[] = []
for (const file of files) {
- const result = await handleFileUpload(file)
+ const handler = props.fileType === 'image' ? handleImageUpload : handleFileUpload
+ const result = await handler(file)
results.push(result)
}
props.onUpload(results)
diff --git a/src/components/_shared/FloatingPanel/FloatingPanel.module.scss b/src/components/_shared/FloatingPanel/FloatingPanel.module.scss
index b21e0326..f5e72ec7 100644
--- a/src/components/_shared/FloatingPanel/FloatingPanel.module.scss
+++ b/src/components/_shared/FloatingPanel/FloatingPanel.module.scss
@@ -3,18 +3,14 @@
bottom: 20px;
left: 0;
right: 0;
-
display: none;
align-items: center;
justify-content: space-between;
-
max-width: 430px;
width: auto;
height: auto;
-
margin: 0 auto;
padding: 14px;
-
background-color: var(--background-color);
border: 2px solid black;
diff --git a/src/components/_shared/Image/Image.tsx b/src/components/_shared/Image/Image.tsx
index fa957458..df5ae79c 100644
--- a/src/components/_shared/Image/Image.tsx
+++ b/src/components/_shared/Image/Image.tsx
@@ -1,9 +1,16 @@
import { splitProps } from 'solid-js'
import type { JSX } from 'solid-js'
-import { imageProxy } from '../../../utils/imageProxy'
+import { getImageUrl } from '../../../utils/getImageUrl'
-export const Image = (props: JSX.ImgHTMLAttributes
) => {
- const [local, others] = splitProps(props, ['src'])
-
- return
+type Props = JSX.ImgHTMLAttributes & {
+ width: number
+ alt: string
+}
+
+export const Image = (props: Props) => {
+ const [local, others] = splitProps(props, ['src', 'alt'])
+
+ const src = getImageUrl(local.src, { width: others.width })
+
+ return
}
diff --git a/src/components/_shared/Popup/Popup.module.scss b/src/components/_shared/Popup/Popup.module.scss
index 79985fd6..0bdde227 100644
--- a/src/components/_shared/Popup/Popup.module.scss
+++ b/src/components/_shared/Popup/Popup.module.scss
@@ -31,6 +31,7 @@
&.bordered {
@include font-size(1.6rem);
+
border: 2px solid #000;
padding: 2.4rem;
@@ -45,6 +46,7 @@
&.tiny {
@include font-size(1.4rem);
+
box-shadow: 0 4px 60px rgb(0 0 0 / 10%);
padding: 1rem;
diff --git a/src/components/_shared/SolidSwiper/SolidSwiper.tsx b/src/components/_shared/SolidSwiper/SolidSwiper.tsx
index 0c670873..faad65d5 100644
--- a/src/components/_shared/SolidSwiper/SolidSwiper.tsx
+++ b/src/components/_shared/SolidSwiper/SolidSwiper.tsx
@@ -9,14 +9,15 @@ import { createFileUploader } from '@solid-primitives/upload'
import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper'
import { SwiperRef } from './swiper'
import { validateFiles } from '../../../utils/validateFile'
-import { handleFileUpload } from '../../../utils/handleFileUpload'
import { useSnackbar } from '../../../context/snackbar'
import { Loading } from '../Loading'
-import { imageProxy } from '../../../utils/imageProxy'
import { clsx } from 'clsx'
import styles from './Swiper.module.scss'
import { composeMediaItems } from '../../../utils/composeMediaItems'
import SimplifiedEditor from '../../Editor/SimplifiedEditor'
+import { handleImageUpload } from '../../../utils/handleImageUpload'
+import { getImageUrl } from '../../../utils/getImageUrl'
+import { Image } from '../Image'
type Props = {
images: MediaItem[]
@@ -95,7 +96,7 @@ export const SolidSwiper = (props: Props) => {
setLoading(true)
const results: UploadedFile[] = []
for (const file of selectedFiles) {
- const result = await handleFileUpload(file)
+ const result = await handleImageUpload(file)
results.push(result)
}
props.onImagesAdd(composeMediaItems(results))
@@ -172,7 +173,7 @@ export const SolidSwiper = (props: Props) => {
// @ts-ignore
-
})
+
{(triggerRef: (el) => void) => (
@@ -232,7 +233,9 @@ export const SolidSwiper = (props: Props) => {
diff --git a/src/components/_shared/SolidSwiper/Swiper.module.scss b/src/components/_shared/SolidSwiper/Swiper.module.scss
index 33a4eb1e..44ecd210 100644
--- a/src/components/_shared/SolidSwiper/Swiper.module.scss
+++ b/src/components/_shared/SolidSwiper/Swiper.module.scss
@@ -82,6 +82,10 @@ $navigation-reserve: 32px;
&.editorMode {
color: #0d0d0d;
+
+ .holder {
+ width: 100%;
+ }
}
.action {
@@ -112,6 +116,7 @@ $navigation-reserve: 32px;
.counter {
@include font-size(1.2rem);
+
position: absolute;
z-index: 2;
top: 477px;
@@ -139,11 +144,6 @@ $navigation-reserve: 32px;
display: flex;
}
}
- &.editorMode {
- .holder {
- width: 100%;
- }
- }
.navigation {
background: rgb(0 0 0 / 40%);
@@ -253,7 +253,9 @@ $navigation-reserve: 32px;
background-color: var(--placeholder-color-semi);
opacity: 0.5;
filter: grayscale(1);
- transition: filter 0.3s ease-in-out, opacity 0.5s ease-in-out;
+ transition:
+ filter 0.3s ease-in-out,
+ opacity 0.5s ease-in-out;
.thumbAction {
display: none;
diff --git a/src/pages/index.page.tsx b/src/pages/index.page.tsx
index d3aada42..c086cb46 100644
--- a/src/pages/index.page.tsx
+++ b/src/pages/index.page.tsx
@@ -18,8 +18,10 @@ export const HomePage = (props: PageProps) => {
return
}
- await loadShouts({ filters: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT })
- await loadRandomTopics({ amount: RANDOM_TOPICS_COUNT })
+ await Promise.all([
+ loadShouts({ filters: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT }),
+ loadRandomTopics({ amount: RANDOM_TOPICS_COUNT })
+ ])
setIsLoaded(true)
})
diff --git a/src/pages/profile/Settings.module.scss b/src/pages/profile/Settings.module.scss
index a6ce9407..54d620e3 100644
--- a/src/pages/profile/Settings.module.scss
+++ b/src/pages/profile/Settings.module.scss
@@ -17,6 +17,14 @@ h5 {
margin-top: 3rem;
}
+.error {
+ @include font-size(1.6rem);
+
+ text-align: center;
+ color: var(--danger-color);
+ margin-top: 1.6rem;
+}
+
.multipleControlsItem {
position: relative;
@@ -133,7 +141,9 @@ h5 {
color: #000;
display: flex;
padding: 0.8em 1em;
- transition: background-color 0.3s, color 0.3s;
+ transition:
+ background-color 0.3s,
+ color 0.3s;
&:hover {
background: #000;
diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx
index 3d8de392..c5251456 100644
--- a/src/pages/profile/profileSettings.page.tsx
+++ b/src/pages/profile/profileSettings.page.tsx
@@ -12,19 +12,20 @@ import { useSession } from '../../context/session'
import FloatingPanel from '../../components/_shared/FloatingPanel/FloatingPanel'
import { useSnackbar } from '../../context/snackbar'
import { useLocalize } from '../../context/localize'
-import { handleFileUpload } from '../../utils/handleFileUpload'
import { Userpic } from '../../components/Author/Userpic'
import { createStore } from 'solid-js/store'
import { clone } from '../../utils/clone'
import SimplifiedEditor from '../../components/Editor/SimplifiedEditor'
import { GrowingTextarea } from '../../components/_shared/GrowingTextarea'
import { AuthGuard } from '../../components/AuthGuard'
+import { handleImageUpload } from '../../utils/handleImageUpload'
export const ProfileSettingsPage = () => {
const { t } = useLocalize()
const [addLinkForm, setAddLinkForm] = createSignal
(false)
const [incorrectUrl, setIncorrectUrl] = createSignal(false)
const [isUserpicUpdating, setIsUserpicUpdating] = createSignal(false)
+ const [uploadError, setUploadError] = createSignal(false)
const [isFloatingPanelVisible, setIsFloatingPanelVisible] = createSignal(false)
const {
@@ -63,14 +64,16 @@ export const ProfileSettingsPage = () => {
const { selectFiles } = createFileUploader({ multiple: false, accept: 'image/*' })
const handleAvatarClick = async () => {
- await selectFiles(async ([uploadFile]) => {
+ selectFiles(async ([uploadFile]) => {
try {
+ setUploadError(false)
setIsUserpicUpdating(true)
- const result = await handleFileUpload(uploadFile)
+ const result = await handleImageUpload(uploadFile)
updateFormField('userpic', result.url)
setIsUserpicUpdating(false)
setIsFloatingPanelVisible(true)
} catch (error) {
+ setUploadError(true)
console.error('[upload avatar] error', error)
}
})
@@ -131,6 +134,9 @@ export const ProfileSettingsPage = () => {
onClick={handleAvatarClick}
loading={isUserpicUpdating()}
/>
+
+ {t('Upload error')}
+
{t('Name')}
diff --git a/src/utils/config.ts b/src/utils/config.ts
index e0c3788b..3039e213 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -3,4 +3,7 @@ export const isDev = import.meta.env.MODE === 'development'
const defaultApiUrl = 'https://testapi.discours.io'
export const apiBaseUrl = import.meta.env.PUBLIC_API_URL || defaultApiUrl
+const defaultThumborUrl = 'https://images.discours.io'
+export const thumborUrl = import.meta.env.PUBLIC_THUMBOR_URL || defaultThumborUrl
+
export const SENTRY_DSN = import.meta.env.PUBLIC_SENTRY_DSN || ''
diff --git a/src/utils/getImageUrl.ts b/src/utils/getImageUrl.ts
new file mode 100644
index 00000000..a36b4544
--- /dev/null
+++ b/src/utils/getImageUrl.ts
@@ -0,0 +1,29 @@
+import { thumborUrl } from './config'
+
+const getSizeUrlPart = (options: { width?: number; height?: number } = {}) => {
+ const widthString = options.width ? options.width.toString() : ''
+ const heightString = options.height ? options.height.toString() : ''
+
+ if (!widthString && !heightString) {
+ return ''
+ }
+
+ return `${widthString}x${heightString}/`
+}
+
+// I'm not proud of this
+export const getImageUrl = (src: string, options: { width?: number; height?: number } = {}) => {
+ const sizeUrlPart = getSizeUrlPart(options)
+
+ if (!src.startsWith(thumborUrl)) {
+ return `${thumborUrl}/unsafe/${sizeUrlPart}${src}`
+ }
+
+ if (src.startsWith(`${thumborUrl}/unsafe`)) {
+ const thumborKey = src.replace(`${thumborUrl}/unsafe`, '')
+ return `${thumborUrl}/unsafe/${sizeUrlPart}${thumborKey}`
+ }
+
+ const thumborKey = src.replace(`${thumborUrl}`, '')
+ return `${thumborUrl}/${sizeUrlPart}${thumborKey}`
+}
diff --git a/src/utils/handleImageUpload.ts b/src/utils/handleImageUpload.ts
new file mode 100644
index 00000000..92ab03a3
--- /dev/null
+++ b/src/utils/handleImageUpload.ts
@@ -0,0 +1,22 @@
+import { UploadFile } from '@solid-primitives/upload'
+import { UploadedFile } from '../pages/types'
+import { thumborUrl } from './config'
+
+export const handleImageUpload = async (uploadFile: UploadFile): Promise => {
+ const formData = new FormData()
+ formData.append('media', uploadFile.file, uploadFile.name)
+ const response = await fetch(`${thumborUrl}/image`, {
+ method: 'POST',
+ body: formData
+ })
+
+ const location = response.headers.get('Location')
+
+ const url = `${thumborUrl}/unsafe/production${location.slice(0, location.lastIndexOf('/'))}`
+ const originalFilename = location.slice(location.lastIndexOf('/') + 1)
+
+ return {
+ originalFilename,
+ url
+ }
+}
diff --git a/src/utils/imageProxy.ts b/src/utils/imageProxy.ts
deleted file mode 100644
index c48530e6..00000000
--- a/src/utils/imageProxy.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { isDev } from './config'
-export const imageProxy = (url: string) => {
- return `${isDev ? 'https://new.discours.io' : ''}/api/image?url=${encodeURI(url)}`
-}
-
-export const audioProxy = (url: string) => {
- return `${isDev ? 'https://new.discours.io' : ''}/api/audio?url=${encodeURI(url)}`
-}
diff --git a/src/utils/renderUploadedImage.ts b/src/utils/renderUploadedImage.ts
index 40b95f14..e4e61279 100644
--- a/src/utils/renderUploadedImage.ts
+++ b/src/utils/renderUploadedImage.ts
@@ -1,5 +1,4 @@
import { UploadedFile } from '../pages/types'
-import { imageProxy } from './imageProxy'
import { hideModal } from '../stores/ui'
import { Editor } from '@tiptap/core'
@@ -22,7 +21,7 @@ export const renderUploadedImage = (editor: Editor, image: UploadedFile) => {
{
type: 'image',
attrs: {
- src: imageProxy(image.url)
+ src: image.url
}
}
]