Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Commit

Permalink
feature/settings and stuff (#153)
Browse files Browse the repository at this point in the history
* add apis for delete and update settings

* add settings page
  • Loading branch information
mgilangjanuar authored Jan 2, 2022
1 parent 0c9e5cf commit 67911e2
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 9 deletions.
3 changes: 1 addition & 2 deletions server/src/api/v1/Contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export class Contact {
const { from, message } = req.body
await axios.post(`https://api.telegram.org/bot${process.env.TG_BOT_TOKEN}/sendMessage`, {
chat_id: process.env.TG_BOT_OWNER_ID,
text: `🛎 *Someone wants to contact you!*\n\n_From: @${from}_\n\n${message}`,
parse_mode: 'markdown'
text: `🛎 @${from} wants to contact you!\n\n${message}`
})
return res.send({ success: true })
}
Expand Down
28 changes: 28 additions & 0 deletions server/src/api/v1/Users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Api } from '@mgilangjanuar/telegram'
import axios from 'axios'
import { Request, Response } from 'express'
import moment from 'moment'
import { Files } from '../../model/entities/Files'
import { Usages } from '../../model/entities/Usages'
import { Users as Model } from '../../model/entities/Users'
import { Midtrans, TransactionDetails } from '../../service/Midtrans'
Expand Down Expand Up @@ -132,6 +134,32 @@ export class Users {
return res.send({ users, length })
}

@Endpoint.PATCH('/me/settings', { middlewares: [Auth] })
public async settings(req: Request, res: Response): Promise<any> {
const { settings } = req.body
req.user.settings = settings
await req.user.save()
return res.send({ settings: req.user?.settings })
}

@Endpoint.POST('/me/delete', { middlewares: [Auth] })
public async remove(req: Request, res: Response): Promise<any> {
const { reason, agreement } = req.body
if (agreement !== 'permanently removed') {
throw { status: 400, body: { error: 'Invalid agreement' } }
}
if (reason) {
await axios.post(`https://api.telegram.org/bot${process.env.TG_BOT_TOKEN}/sendMessage`, {
chat_id: process.env.TG_BOT_OWNER_ID,
text: `😭 ${req.user.name} (@${req.user.username}) removed their account.\n\nReason: ${reason}`
})
}
await Files.delete({ user_id: req.user.id })
await req.user.remove()
const success = await req.tg.invoke(new Api.auth.LogOut())
return res.clearCookie('authorization').clearCookie('refreshToken').send({ success })
}

// @Endpoint.USE('/upgradePlans', { middlewares: [Auth] })
// public async upgradePlans(req: Request, res: Response): Promise<any> {
// if (req.user.username !== 'mgilangjanuar') {
Expand Down
5 changes: 5 additions & 0 deletions server/src/model/entities/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ export class Users extends BaseModelWithID {

@OneToMany(() => Files, files => files.user)
files?: Files[]

@Column('jsonb', { default: null })
settings?: {
expandable_rows?: boolean
}
}
4 changes: 4 additions & 0 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import 'antd-country-phone-input/dist/index.css'
const Dashboard = lazy(
() => import(/* webpackChunkName: 'DashboardPage' */ './pages/dashboard')
)
const Settings = lazy(
() => import(/* webpackChunkName: 'SettingsPage' */ './pages/Settings')
)
const Home = lazy(
() => import(/* webpackChunkName: 'HomePage' */ './pages/Home')
)
Expand Down Expand Up @@ -59,6 +62,7 @@ function App(): React.ReactElement {
/> : <Suspense fallback={<></>}>
<Switch>
<Route path="/dashboard/:type?" exact component={Dashboard} />
<Route path="/settings" exact component={Settings} />
<Route path="/view/:id" exact component={View} />
<Route path="/login" exact component={Login} />
<Route path="/terms" exact component={Terms} />
Expand Down
6 changes: 3 additions & 3 deletions web/src/pages/Refund.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { fetcher } from '../utils/Fetcher'
import Footer from './components/Footer'
import Navbar from './components/Navbar'

const Terms: React.FC = () => {
const Refund: React.FC = () => {
const { data } = useSWRImmutable('/documents/refund', fetcher)
const { data: me } = useSWRImmutable('/users/me', fetcher)

return <>
<Navbar page="terms" user={me} />
<Navbar page="refund" user={me} />
<Layout.Content className="container">
<Row>
<Col lg={{ span: 18, offset: 3 }} md={{ span: 20, offset: 2 }} span={24}>
Expand All @@ -26,4 +26,4 @@ const Terms: React.FC = () => {
</>
}

export default Terms
export default Refund
112 changes: 112 additions & 0 deletions web/src/pages/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { DeleteOutlined, LogoutOutlined, WarningOutlined } from '@ant-design/icons'
import { Avatar, Button, Card, Col, Divider, Form, Input, Layout, Modal, notification, Row, Switch, Typography } from 'antd'
import { useForm } from 'antd/es/form/Form'
import React, { useState } from 'react'
import { useHistory } from 'react-router-dom'
import useSWRImmutable from 'swr/immutable'
import { apiUrl, fetcher, req } from '../utils/Fetcher'
import Footer from './components/Footer'
import Navbar from './components/Navbar'

const Settings: React.FC = () => {
const history = useHistory()
const [expandableRows, setExpandableRows] = useState<boolean>(false)
const [logoutConfirmation, setLogoutConfirmation] = useState<boolean>(false)
const [removeConfirmation, setRemoveConfirmation] = useState<boolean>(false)
const [formRemoval] = useForm()
const { data: me } = useSWRImmutable('/users/me', fetcher, {
onError: () => history.push('/login'),
onSuccess: ({ user }) => {
setExpandableRows(user?.settings?.expandable_rows)
}
})

const save = (settings: any) => {
req.patch('/users/me/settings', { settings })
.then(() => notification.success({ message: 'Settings saved' }))
.catch(() => notification.error({ message: 'Something error. Please try again.' }))
}

const logout = async () => {
await req.post('/auth/logout')
return window.location.replace('/')
}

const remove = async () => {
const { agreement, reason } = formRemoval.getFieldsValue()
try {
await req.post('/users/me/delete', { agreement, reason })
return window.location.replace('/')
} catch (error: any) {
return notification.error({ message: 'Error', description: error.response?.data.error })
}
}

return <>
<Navbar page="settings" user={me} />
<Layout.Content className="container">
<Row style={{ marginTop: '30px' }}>
<Col lg={{ span: 10, offset: 7 }} md={{ span: 14, offset: 5 }} span={20} offset={2}>
<Typography.Title level={2}>
Settings
</Typography.Title>
<Card>
<Card.Meta avatar={<Avatar size="large" src={`${apiUrl}/users/me/photo`} />} title={me?.user.name} description={me?.user.username} />
<Divider />
<Form layout="horizontal" labelAlign="left" labelCol={{ span: 12 }} wrapperCol={{ span: 12 }}>
<Form.Item label="Expandable Rows" name="expandable_rows">
<Switch onChange={val => {
setExpandableRows(val)
save({ expandable_rows: val })
}} checked={expandableRows} />
</Form.Item>
<Form.Item label={<Typography.Text type="danger">Delete Account</Typography.Text>}>
<Button shape="round" danger type="primary" icon={<DeleteOutlined />} onClick={() => setRemoveConfirmation(true)}>Permanently Removed</Button>
</Form.Item>
</Form>
<Row>
<Col span={24} md={{ span: 12, offset: 12 }} lg={{ span: 12, offset: 12 }}>
<Button icon={<LogoutOutlined />} danger shape="round" onClick={() => setLogoutConfirmation(true)}>
Logout
</Button>
</Col>
</Row>
</Card>
</Col>
</Row>
</Layout.Content>

<Modal title={<Typography.Text>
<Typography.Text type="warning"><WarningOutlined /></Typography.Text> Confirmation
</Typography.Text>}
visible={logoutConfirmation}
onCancel={() => setLogoutConfirmation(false)}
onOk={logout}
okButtonProps={{ danger: true, type: 'primary' }}>
<Typography.Paragraph>
All the files you share will not be able to download once you sign out. Continue?
</Typography.Paragraph>
</Modal>

<Modal title={<Typography.Text>
<Typography.Text type="warning"><WarningOutlined /></Typography.Text> This action cannot be undone
</Typography.Text>}
visible={removeConfirmation}
onCancel={() => setRemoveConfirmation(false)}
onOk={remove}
okButtonProps={{ danger: true, type: 'primary' }}>
<Form form={formRemoval} onFinish={remove} layout="vertical">
<Form.Item name="reason" label="Reason" rules={[{ required: true, message: 'Please input your username' }]}>
<Input.TextArea />
</Form.Item>
<Form.Item name="agreement" label={<>Please type &nbsp; <Typography.Text type="danger">permanently removed</Typography.Text> &nbsp; for your confirmation</>} rules={[{ required: true, message: 'Please input your username' }]}>
<Input />
</Form.Item>
</Form>
</Modal>

<Footer />
</>
}

export default Settings
5 changes: 3 additions & 2 deletions web/src/pages/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CrownOutlined, DashboardOutlined, LoginOutlined, LogoutOutlined, MenuOutlined, UserOutlined, WarningOutlined } from '@ant-design/icons'
import { CrownOutlined, DashboardOutlined, LoginOutlined, LogoutOutlined, MenuOutlined, SettingOutlined, UserOutlined, WarningOutlined } from '@ant-design/icons'
import { Button, Layout, Menu, Modal, Popover, Progress, Tag, Tooltip, Typography } from 'antd'
import moment from 'moment'
import prettyBytes from 'pretty-bytes'
Expand Down Expand Up @@ -44,11 +44,12 @@ const Navbar: React.FC<Props> = ({ user, page }) => {
<div style={{ padding: '10px' }}>
Bandwidth usage: { }
{(user?.user?.plan || user?.plan) === 'premium' ? <Tag color="green">Unlimited</Tag> : <Tooltip placement="left" title={<>You can download up to {prettyBytes(Math.max(0, 1_500_000_000 - Number(usage?.usage.usage)))} until {moment(usage?.usage.expire).local().format('lll')}</>}>
<Progress status="exception" percent={Number((Number(usage?.usage.usage) / 1_500_000_000 * 100).toFixed(1))} />
<Progress status="exception" percent={Number((Number(usage?.usage.usage || 0) / 1_500_000_000 * 100).toFixed(1))} />
</Tooltip>}
</div>
<Menu>
<Menu.Item key="dashboard" icon={<DashboardOutlined />} onClick={() => history.push('/dashboard')}>Dashboard</Menu.Item>
<Menu.Item key="settings" icon={<SettingOutlined />} onClick={() => history.push('/settings')}>Settings</Menu.Item>
<Menu.Item danger key="logout" icon={<LogoutOutlined />} onClick={() => setLogoutConfirmation(true)}>Logout</Menu.Item>
</Menu>
</div>}>
Expand Down
4 changes: 3 additions & 1 deletion web/src/pages/dashboard/components/TableFiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { apiUrl } from '../../../utils/Fetcher'
interface Props {
files?: any,
tab: string,
me?: any,
onChange: (...args: any[]) => void,
onDelete: (row: any) => void,
onRename: (row: any) => void,
Expand All @@ -48,6 +49,7 @@ interface Props {
const TableFiles: React.FC<Props> = ({
files,
tab,
me,
onChange,
onDelete,
onRename,
Expand Down Expand Up @@ -307,7 +309,7 @@ const TableFiles: React.FC<Props> = ({
})
}
})}
expandable={window.innerWidth < 752 ? {
expandable={me?.settings?.expandable_rows && window.innerWidth < 752 ? {
expandedRowRender: (row: any) => <Descriptions labelStyle={{ fontWeight: 'bold' }} column={1}>
<Descriptions.Item label="Size">{row.size ? prettyBytes(Number(row.size)) : '-'}</Descriptions.Item>
<Descriptions.Item label="Uploaded At">{row.upload_progress !== null ? <>Uploading {Number((row.upload_progress * 100).toFixed(2))}%</> : moment(row.uploaded_at).local().format('lll')}</Descriptions.Item>
Expand Down
10 changes: 9 additions & 1 deletion web/src/pages/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { FolderAddOutlined, HomeOutlined, PlusOutlined, SyncOutlined, UploadOutlined, WarningOutlined } from '@ant-design/icons'
import {
FolderAddOutlined,
HomeOutlined,
PlusOutlined,
SyncOutlined,
UploadOutlined,
WarningOutlined
} from '@ant-design/icons'
import {
Alert,
Button,
Expand Down Expand Up @@ -344,6 +351,7 @@ const Dashboard: React.FC<PageProps> = ({ match }) => {
<TableFiles
files={files}
tab={tab}
me={me?.user}
onChange={change}
onDelete={row => {
if (!selected?.find(select => select.id === row.id)) {
Expand Down

0 comments on commit 67911e2

Please sign in to comment.