Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
dexfilter-web-nuxt3
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
haojie
dexfilter-web-nuxt3
Commits
b37a71d4
Commit
b37a71d4
authored
Mar 23, 2023
by
lei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1
parent
0a26e3c7
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
185 additions
and
159 deletions
+185
-159
nuxt.config.ts
+7
-13
plugins/Tdesign.ts
+0
-6
utils/plugin/ShowPlugin.ts
+27
-0
views/analysis/detailsEcharts.vue
+64
-66
views/analysis/index.vue
+31
-30
views/token/Collection.vue
+1
-0
views/token/RightDetail.vue
+12
-9
views/token/test/customLoader.vue
+4
-0
views/user/InvitedDialog.vue
+7
-6
views/user/memberCenter.vue
+32
-29
No files found.
nuxt.config.ts
View file @
b37a71d4
...
...
@@ -76,19 +76,13 @@ export default defineNuxtConfig({
comments
:
false
,
},
},
rollupOptions
:
{
output
:
{
manualChunks
(
id
,
{
getModuleInfo
})
{
// 折线图
if
(
id
.
includes
(
"echarts"
))
{
return
"CustomEcharts"
;
}
else
if
(
id
.
includes
(
"ethers"
))
{
return
"ethers"
;
}
// 合并小文件
},
},
},
// rollupOptions: {
// output: {
// manualChunks: {
// CustomEcharts: ["echarts"],
// },
// },
// },
sourcemap
:
false
,
// 关闭文件计算
reportCompressedSize
:
false
,
...
...
plugins/Tdesign.ts
View file @
b37a71d4
...
...
@@ -19,10 +19,7 @@ import {
RadioGroup
as
TRadioGroup
,
RadioButton
as
TRadioButton
,
InputNumber
as
TInputNumber
,
Row
as
TRow
,
Col
as
TCol
,
Checkbox
as
TCheckbox
,
Divider
as
TDivider
,
Swiper
as
TSwiper
,
SwiperItem
as
TSwiperItem
,
}
from
"tdesign-vue-next"
;
...
...
@@ -46,10 +43,7 @@ const components = [
TRadioGroup
,
TRadioButton
,
TInputNumber
,
TRow
,
TCol
,
TCheckbox
,
TDivider
,
TSwiper
,
TSwiperItem
,
];
...
...
utils/plugin/ShowPlugin.ts
0 → 100644
View file @
b37a71d4
interface
instruct
{
arg
:
any
;
dir
:
any
;
instance
:
any
;
modifiers
:
any
;
oldValue
?:
number
;
value
?:
number
;
}
const
sleep
=
(
time
:
number
)
=>
{
const
StartTime
=
new
Date
().
valueOf
();
while
(
true
)
{
// 获取当前时间
const
CurrentTime
=
new
Date
().
valueOf
();
if
(
CurrentTime
-
StartTime
>=
time
)
{
break
;
}
}
};
// 自定义指令
export
const
vDelayHide
=
{
mounted
:
(
el
:
HTMLDivElement
,
{
value
}:
instruct
)
=>
{
// 指定时间隐藏
setTimeout
(()
=>
{
el
.
style
.
display
=
"none"
;
},
value
);
},
};
views/analysis/detailsEcharts.vue
View file @
b37a71d4
<
template
>
<div>
<div
class=
"echart-box-cl narrow-scrollbar"
id=
"home_detail_echarts"
>
<ClientOnly>
<div
class=
"echart-box-cl narrow-scrollbar"
>
<holder-box
v-if=
"mt != '2'"
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></holder-box>
<deal-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></deal-box>
<in-and-out-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></in-and-out-box>
<top100-box
v-if=
"mt != '2'"
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></top100-box>
<pool-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></pool-box>
<twitter-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></twitter-box>
<telegram-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></telegram-box>
<discord-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></discord-box>
<twitter-total-box
:token=
"token"
:currentPath=
"currentPath"
></twitter-total-box>
</div>
<holder-box
v-if=
"mt != '2'"
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></holder-box>
<deal-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></deal-box>
<in-and-out-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></in-and-out-box>
<top100-box
v-if=
"mt != '2'"
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></top100-box>
<pool-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></pool-box>
<twitter-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></twitter-box>
<telegram-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></telegram-box>
<discord-box
:token=
"token"
:tb=
"tb"
:currentPath=
"currentPath"
:r24h=
"r24h"
></discord-box>
<twitter-total-box
:token=
"token"
:currentPath=
"currentPath"
></twitter-total-box>
</ClientOnly>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
HolderBox
from
'./HolderBox.vue'
;
import
DealBox
from
'./DealBox.vue'
;
import
InAndOutBox
from
'./InAndOutBox.vue'
;
import
TwitterBox
from
'./TwitterBox.vue'
;
import
TwitterTotalBox
from
'./TwitterTotalBox.vue'
;
import
Top100Box
from
'./Top100Box.vue'
;
import
PoolBox
from
'./PoolBox.vue'
;
import
TelegramBox
from
'./TelegramBox.vue'
;
import
DiscordBox
from
'./DiscordBox.vue'
;
import
HolderBox
from
"./HolderBox.vue"
;
import
DealBox
from
"./DealBox.vue"
;
import
InAndOutBox
from
"./InAndOutBox.vue"
;
import
TwitterBox
from
"./TwitterBox.vue"
;
import
TwitterTotalBox
from
"./TwitterTotalBox.vue"
;
import
Top100Box
from
"./Top100Box.vue"
;
import
PoolBox
from
"./PoolBox.vue"
;
import
TelegramBox
from
"./TelegramBox.vue"
;
import
DiscordBox
from
"./DiscordBox.vue"
;
const
props
=
defineProps
({
token
:
{
type
:
String
,
...
...
views/analysis/index.vue
View file @
b37a71d4
...
...
@@ -104,22 +104,23 @@
</div>
</template>
<
script
setup
lang=
"ts"
>
import
MyLayout
from
'@/views/layout/layout.vue'
;
import
RightDetail
from
'@/views/token/RightDetail.vue'
;
import
HolderBox
from
'/views/analysis/HolderBox.vue'
;
import
DealBox
from
'/views/analysis/DealBox.vue'
;
import
InAndOutBox
from
'/views/analysis/InAndOutBox.vue'
;
import
TwitterBox
from
'/views/analysis/TwitterBox.vue'
;
import
TwitterTotalBox
from
'/views/analysis/TwitterTotalBox.vue'
;
import
Top100Box
from
'/views/analysis/Top100Box.vue'
;
import
PoolBox
from
'/views/analysis/PoolBox.vue'
;
import
TelegramBox
from
'/views/analysis/TelegramBox.vue'
;
import
DiscordBox
from
'/views/analysis/DiscordBox.vue'
;
import
request
from
'@/utils/request'
;
import
{
WebS
}
from
'@/utils/TokenTrans'
;
import
{
filterChainObj
}
from
'@/constants/UnifiedManagementChain'
;
import
{
webLogo
}
from
'@/constants/logo'
;
import
{
useI18n
}
from
'vue-i18n'
;
import
MyLayout
from
"@/views/layout/layout.vue"
;
import
RightDetail
from
"@/views/token/RightDetail.vue"
;
import
HolderBox
from
"/views/analysis/HolderBox.vue"
;
import
DealBox
from
"/views/analysis/DealBox.vue"
;
import
InAndOutBox
from
"/views/analysis/InAndOutBox.vue"
;
import
TwitterBox
from
"/views/analysis/TwitterBox.vue"
;
import
TwitterTotalBox
from
"/views/analysis/TwitterTotalBox.vue"
;
import
Top100Box
from
"/views/analysis/Top100Box.vue"
;
import
PoolBox
from
"/views/analysis/PoolBox.vue"
;
import
TelegramBox
from
"/views/analysis/TelegramBox.vue"
;
import
DiscordBox
from
"/views/analysis/DiscordBox.vue"
;
import
request
from
"@/utils/request"
;
import
{
WebS
}
from
"@/utils/TokenTrans"
;
import
{
filterChainObj
}
from
"@/constants/UnifiedManagementChain"
;
import
{
webLogo
}
from
"@/constants/logo"
;
import
{
useI18n
}
from
"vue-i18n"
;
import
{
Row
as
TRow
,
Col
as
TCol
}
from
"tdesign-vue-next"
;
const
theme
=
useCurTheme
();
const
{
locale
}
=
useI18n
();
// 初始化折线图
...
...
@@ -132,27 +133,27 @@ watch(
);
const
route
=
useRoute
();
const
routeTb
=
route
.
params
.
tb
;
const
tb
=
ref
(
''
);
const
currentPath
=
ref
(
route
.
params
.
chain
+
''
);
const
tb
=
ref
(
""
);
const
currentPath
=
ref
(
route
.
params
.
chain
+
""
);
const
chain
=
useChain
();
// token---接口得到
const
token
=
ref
(
''
);
const
token
=
ref
(
""
);
// r24h--接口
const
r24h
=
ref
();
useHead
({
title
:
`Dexfilter | analysis`
,
meta
:
[
{
name
:
'description'
,
content
:
`Dexfilter 数据分析页
${
route
.
params
.
tb
??
'---'
}
`,
name
:
"description"
,
content
:
`Dexfilter 数据分析页
${
route
.
params
.
tb
??
"---"
}
`,
},
],
link: [webLogo],
});
const twitter = ref(
''
);
const options = [
'5M', '30M', '1H', '6H', '1D'
];
const twitter = ref(
""
);
const options = [
"5M", "30M", "1H", "6H", "1D"
];
// 先取出对应链的接口id
let Obj = filterChainObj(
'name'
, currentPath.value);
let Obj = filterChainObj(
"name"
, currentPath.value);
currentPath.value = Obj.value;
onBeforeMount(() => {
if (currentPath.value) {
...
...
@@ -168,10 +169,10 @@ onBeforeMount(() => {
.
then
((
res
:
any
)
=>
{
if
(
!
res
.
is_token
)
{
tb
.
value
=
res
.
pair
[
0
]
==
'd'
?
res
.
pair
.
slice
(
1
,
res
.
pair
.
length
)
:
res
.
pair
;
res
.
pair
[
0
]
==
"d"
?
res
.
pair
.
slice
(
1
,
res
.
pair
.
length
)
:
res
.
pair
;
}
else
{
tb
.
value
=
res
.
tbname
[
0
]
==
'd'
res
.
tbname
[
0
]
==
"d"
?
res
.
tbname
.
slice
(
1
,
res
.
tbname
.
length
)
:
res
.
tbname
;
}
...
...
@@ -182,10 +183,10 @@ onBeforeMount(() => {
});
onMounted
(()
=>
{
nextTick
(()
=>
{
let
echartBox
=
window
.
sessionStorage
.
getItem
(
'echart-box'
);
if
(
echartBox
!==
'null'
&&
echartBox
!==
''
&&
echartBox
!==
null
)
{
let
echartBox
=
window
.
sessionStorage
.
getItem
(
"echart-box"
);
if
(
echartBox
!==
"null"
&&
echartBox
!==
""
&&
echartBox
!==
null
)
{
let
dom
=
document
.
querySelector
(
`#
${
echartBox
}
`
);
dom
.
classList
.
add
(
'active'
);
dom
.
classList
.
add
(
"active"
);
}
});
});
...
...
@@ -194,7 +195,7 @@ const SettwitterRul = (value: string) => {
};
</
script
>
<
style
lang=
"less"
scoped
>
@import
'@/style/variables.less'
;
@import
"@/style/variables.less"
;
.token-analysis-wrapper
{
box-sizing
:
border-box
;
padding
:
12px
0px
0
12px
;
...
...
views/token/Collection.vue
View file @
b37a71d4
...
...
@@ -376,6 +376,7 @@ onMounted(() => {
.custom-home-collection-box
{
background-color
:
var
(
--td--right-back-color-2
);
border
:
var
(
--new-border-2
);
border-top
:
none
;
border-radius
:
0
0
@
border-radius
@
border-radius
;
position
:
relative
;
overflow-y
:
auto
;
...
...
views/token/RightDetail.vue
View file @
b37a71d4
...
...
@@ -96,6 +96,7 @@
</
template
>
<
template
v-if=
"ifDetails"
>
<div
v-show=
"defaBtn == 'echart'"
class=
"all-echarts"
>
<Animation
v-delay-hide=
"300"
></Animation>
<details-echarts
:token=
"props.token"
:tb=
"tb"
...
...
@@ -124,17 +125,15 @@ import { useI18n } from "vue-i18n";
import
RightDetailHeader
from
"./rightDetailHeader.vue"
;
import
SubmitSocialInfo
from
"./SubmitSocialInfo.vue"
;
import
Collection
from
"./Collection.vue"
;
import
Animation
from
"@/components/Animation.vue"
;
import
{
vDelayHide
}
from
"@/utils/plugin/ShowPlugin"
;
let
DetailsEcharts
:
any
=
null
;
// 异步组件
// const DetailsEcharts = defineAsyncComponent(
// () => import("../analysis/detailsEcharts.vue")
// );
onMounted
(()
=>
{
console
.
log
(
"导入"
);
console
.
log
(
DetailsEcharts
);
// 加载完成后导入echarts
DetailsEcharts
=
()
=>
import
(
"../analysis/detailsEcharts.vue"
);
});
if
(
process
.
client
)
{
DetailsEcharts
=
defineAsyncComponent
(
()
=>
import
(
"../analysis/detailsEcharts.vue"
)
);
}
const
router
=
useRouter
();
const
{
t
,
locale
}
=
useI18n
();
// 通知表格收藏图表取消收藏
...
...
@@ -422,6 +421,10 @@ watch(
}
}
}
.all-echarts
{
position
:
relative
;
height
:
calc
(
100vh
-
120px
);
}
.details-title-box
{
width
:
100%
;
.right-body-scroll
{
...
...
views/token/test/customLoader.vue
0 → 100644
View file @
b37a71d4
<
template
>
<div>
我加载了
</div>
</
template
>
<
script
lang=
"ts"
setup
></
script
>
views/user/InvitedDialog.vue
View file @
b37a71d4
...
...
@@ -42,13 +42,14 @@
</template>
<
script
lang=
"ts"
setup
>
import
{
Divider
as
TDivider
}
from
"tdesign-vue-next"
;
const
props
=
defineProps
({
InvitedVisible
:
Boolean
,
});
const
confirmDialog
=
ref
(
false
);
const
emit
=
defineEmits
([
'closeInvitedDialog'
]);
const
emit
=
defineEmits
([
"closeInvitedDialog"
]);
const
closeInvitedDialog
=
()
=>
{
emit
(
'closeInvitedDialog'
,
false
);
emit
(
"closeInvitedDialog"
,
false
);
};
// 关闭确认对话框
const
closeComfrimDialog
=
(
value
:
boolean
)
=>
{
...
...
@@ -65,11 +66,11 @@ const comfimawal = (value: boolean) => {
<
style
lang=
"less"
scoped
>
//
红点
@import
'@/style/components/dot.less'
;
@import
"@/style/components/dot.less"
;
//
第二层
dialog
的蒙层颜色
@import
'@/style/components/hierarchyDialog.less'
;
@import
'@/style/variables.less'
;
@import
'@/style/flex.less'
;
@import
"@/style/components/hierarchyDialog.less"
;
@import
"@/style/variables.less"
;
@import
"@/style/flex.less"
;
.Invited-table
{
.Invited-dialog
{
:deep(.t-dialog__wrap)
{
...
...
views/user/memberCenter.vue
View file @
b37a71d4
...
...
@@ -7,13 +7,13 @@
:src=
"userInfo.avatar ?? '/images/svg/header/userOne.svg'"
alt=
""
/>
<p
class=
"p1-blue"
>
{{
userInfo
.
vip_type
??
'---'
}}
</p>
<p
class=
"p1-blue"
>
{{
userInfo
.
vip_type
??
"---"
}}
</p>
<div
class=
"expire-time"
>
<p>
{{
$t
(
'user.ExpirationDate'
)
}}
</p>
<p>
{{
$t
(
"user.ExpirationDate"
)
}}
</p>
<p
class=
"p1-item"
>
{{
userInfo
.
vip_type
===
'免费会员'
?
$t
(
'user.NotOpen'
)
userInfo
.
vip_type
===
"免费会员"
?
$t
(
"user.NotOpen"
)
:
userInfo
.
vip_time
}}
</p>
...
...
@@ -21,13 +21,13 @@
</div>
<div
class=
"Member-invite"
>
<div
class=
"Member-center1"
>
<h3>
{{
$t
(
'user.Dear'
)
}}
{{
userInfo
.
name
}}
:
</h3>
<h3>
{{
$t
(
"user.Dear"
)
}}
{{
userInfo
.
name
}}
:
</h3>
<div
class=
"Menber-grade"
>
{{
$t
(
'user.PackageLevel'
)
}}
:
<p
class=
"Menber-grade-p1"
>
{{
userInfo
.
vip_type
??
'---'
}}
</p>
{{
$t
(
"user.PackageLevel"
)
}}
:
<p
class=
"Menber-grade-p1"
>
{{
userInfo
.
vip_type
??
"---"
}}
</p>
</div>
<div
class=
"Menber-bound"
v-if=
"!userInfo.is_bind"
>
<p>
{{
$t
(
'user.BindingInvitationCode'
)
}}
:
</p>
<p>
{{
$t
(
"user.BindingInvitationCode"
)
}}
:
</p>
<t-input
v-model=
"BindCode"
type=
"text"
...
...
@@ -37,36 +37,36 @@
<t-button
@
click=
"BindingInCode"
class=
"custom_button_back_border"
>
{{
$t
(
'user.Confirm'
)
}}
</t-button
>
{{
$t
(
"user.Confirm"
)
}}
</t-button
>
</div>
<div
class=
"Menber-bound"
v-else
>
<span
class=
"member-span"
>
{{
$t
(
'user.haveBound'
)
}}
!
</span>
<span
class=
"member-span"
>
{{
$t
(
"user.haveBound"
)
}}
!
</span>
</div>
</div>
<div
class=
"Member-center2"
>
<t-button
class=
"custom_button_back_border"
>
{{
$t
(
'user.BillingDetails'
)
$t
(
"user.BillingDetails"
)
}}
</t-button>
</div>
</div>
</div>
<div
class=
"Member-item2"
>
<h3>
{{
$t
(
'user.PackageOpening'
)
}}
</h3>
<h3>
{{
$t
(
"user.PackageOpening"
)
}}
</h3>
<div
class=
"Member-set-meal"
>
<div
class=
"vip-item"
v-for=
"item in comboData.list"
:key=
"item.title"
>
<p>
{{
item
.
title
}}
</p>
<p>
$
{{
item
.
price
}}
</p>
<p
class=
"delete-p"
>
{{
$t
(
'user.OriginalPrice'
)
}}{{
item
.
origin_price
}}
{{
$t
(
"user.OriginalPrice"
)
}}{{
item
.
origin_price
}}
</p>
<p>
{{
$t
(
'user.MemberDays'
)
}}{{
item
.
vip_days
}}
</p>
<p>
{{
$t
(
"user.MemberDays"
)
}}{{
item
.
vip_days
}}
</p>
<t-divider
class=
"divider-box"
/>
<p
v-for=
"it in item.describe"
:key=
"it"
>
{{
it
}}
</p>
<t-button
@
click=
"payBtn(item.price)"
class=
"custom_button_back_border"
>
{{
$t
(
'user.BuyNow'
)
}}
</t-button
>
{{
$t
(
"user.BuyNow"
)
}}
</t-button
>
</div>
</div>
...
...
@@ -80,29 +80,32 @@
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
PayFox
from
'./payFox.vue'
;
import
request
from
'@/utils/request'
;
import
{
MessagePlugin
}
from
'tdesign-vue-next'
;
import
PayFox
from
"./payFox.vue"
;
import
request
from
"@/utils/request"
;
import
{
MessagePlugin
,
Divider
as
TDivider
}
from
"tdesign-vue-next"
;
// 用户信息
const
userInfo
=
useTokenInfo
();
// 用户token
const
Cookie
=
useCookie
(
'userCookie'
);
const
Cookie
=
useCookie
(
"userCookie"
);
// 绑定邀请码输入框
const
BindCode
=
ref
(
''
);
const
BindCode
=
ref
(
""
);
// 支付弹窗的打开与关闭
const
hasPayDialog
=
ref
(
false
);
onMounted
(()
=>
{
getMemberList
();
});
// 套餐
const
comboData
=
reactive
({
const
comboData
=
reactive
<
{
list
:
any
[];
price
:
string
;
}
>
({
list
:
[],
// 当前选择的价格
price
:
''
,
price
:
""
,
});
// 获取会员列表
const
getMemberList
=
async
()
=>
{
const
res
:
any
=
await
request
.
get
(
'/api/vip/list'
,
{});
const
res
:
any
=
await
request
.
get
(
"/api/vip/list"
,
{});
if
(
res
.
code
===
0
)
{
comboData
.
list
=
res
.
data
.
list
;
}
...
...
@@ -116,7 +119,7 @@ const payBtn = (price: string) => {
const
BindingInCode
=
async
()
=>
{
try
{
const
res
:
any
=
await
request
.
post
(
'/api/user/bind'
,
"/api/user/bind"
,
{
invite_code
:
BindCode
.
value
,
},
...
...
@@ -127,11 +130,11 @@ const BindingInCode = async () => {
}
);
if
(
res
.
code
===
0
)
{
MessagePlugin
.
success
(
'绑定成功'
);
MessagePlugin
.
success
(
"绑定成功"
);
// 修改当前状态
userInfo
.
value
[
'is_bind'
]
=
1
;
userInfo
.
value
[
"is_bind"
]
=
1
;
}
else
{
MessagePlugin
.
warning
(
'绑定失败'
);
MessagePlugin
.
warning
(
"绑定失败"
);
}
}
catch
(
e
)
{}
};
...
...
@@ -142,8 +145,8 @@ const closePayDialog = (value: boolean) => {
</
script
>
<
style
lang=
"less"
scoped
>
@import
'@/style/variables.less'
;
@import
'@/style/flex.less'
;
@import
"@/style/variables.less"
;
@import
"@/style/flex.less"
;
.Member-Center
{
height
:
calc
(
100vh
-
70px
);
overflow-x
:
auto
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment