2024-02-29 04:08:54 +00:00
< template >
2024-03-26 03:14:42 +00:00
< div class = "page" >
< div class = "profile" >
< div class = "profile-header" >
< div class = "user" >
< img
v - if = "profile.avatar"
: src = "profile.avatar"
class = "avatar"
>
< h2 class = "username ellipsis" > { { profile . username } } < / h2 >
< / div >
2024-03-03 01:33:35 +00:00
2024-03-26 23:06:03 +00:00
< span class = "age" > { { formatDistanceStrict ( Date . now ( ) , profile . createdAt ) } } < / span >
2024-03-26 03:14:42 +00:00
< / div >
< section class = "profile-section" >
< div class = "section-header" >
< h3 class = "heading" > Stashes < / h3 >
< button
2024-04-02 03:55:53 +00:00
v - if = "profile.id === user?.id"
2024-03-26 03:14:42 +00:00
class = "button"
@ click = "showStashDialog = true"
>
< Icon icon = "plus3" / >
< span class = "button-label" > New stash < / span >
< / button >
2024-03-21 01:59:55 +00:00
< / div >
2024-03-26 03:14:42 +00:00
< Dialog
v - if = "showStashDialog"
title = "New stash"
@ close = "showStashDialog = false"
@ open = "stashNameInput?.focus()"
>
< form
class = "dialog-body"
@ submit . prevent = "createStash"
>
< input
ref = "stashNameInput"
v - model = "stashName"
2024-03-26 23:06:03 +00:00
maxlength = "24"
2024-03-26 03:14:42 +00:00
placeholder = "Stash name"
2024-03-26 23:06:03 +00:00
class = "input"
2024-03-26 03:14:42 +00:00
>
< button
class = "button button-submit"
> Create stash < / button >
< / form >
< / Dialog >
< ul class = "stashes nolist" >
< li
v - for = "stash in profile.stashes"
: key = "`stash-${stash.id}`"
>
2024-03-26 23:06:03 +00:00
< StashTile
: stash = "stash"
: profile = "profile"
@ reload = "reloadProfile"
/ >
2024-03-26 03:14:42 +00:00
< / li >
< / ul >
< / section >
2024-05-19 03:07:35 +00:00
< section
v - if = "profile.id === user?.id"
class = "profile-section"
>
< div class = "section-header" >
< h3 class = "heading" > Alerts < / h3 >
< button
class = "button"
@ click = "showAlertDialog = true"
>
< Icon icon = "plus3" / >
< span class = "button-label" > New alert < / span >
< / button >
< / div >
2024-05-27 01:06:54 +00:00
< ul class = "alerts nolist" >
< li
v - for = "alert in alerts"
: key = "`alert-${alert.id}`"
class = "alert"
>
2024-05-28 03:49:28 +00:00
< div class = "alert-triggers" >
< Icon
2024-05-28 03:55:14 +00:00
v - tooltip = "alert.notify ? 'Notify in traxxx' : undefined"
2024-05-28 03:49:28 +00:00
icon = "bell2"
: class = "{ trigger: alert.notify }"
/ >
< Icon
v - if = "alert.stashes.some((stash) => !stash.isPrimary)"
2024-05-28 03:55:14 +00:00
v - tooltip = "`Add to ${alert.stashes.map((stash) => stash.name).join(', ')}`"
2024-05-28 03:49:28 +00:00
icon = "folder-heart"
class = "trigger"
/ >
< Icon
v - else
2024-05-28 03:55:14 +00:00
v - tooltip = "alert.stashes.length > 0 ? 'Add to Favorites' : undefined"
2024-05-28 03:49:28 +00:00
icon = "heart7"
: class = "{ trigger: alert.stashes.length > 0 }"
/ >
<!--
< Icon
icon = "envelop5"
title = "E-mail me"
: class = "{ trigger: alert.email }"
/ >
-- >
< / div >
< div
class = "alert-details"
: class = "{ and: alert.and.fields, or: !alert.and.fields }"
>
2024-05-27 01:06:54 +00:00
< span
v - if = "alert.tags.length > 0"
2024-05-28 03:49:28 +00:00
class = "alert-detail alert-tags"
: class = "{ and: alert.and.tags, or: !alert.and.tags }"
>
< span class = "alert-values" >
< span
v - for = "tag in alert.tags"
: key = "`tag-${alert.id}-${tag.id}`"
class = "alert-key"
>
< a
: href = "`/tag/${tag.slug}`"
class = "alert-value link"
> { { tag . name } } < / a >
< / span >
< / span >
< / span >
2024-05-27 01:06:54 +00:00
< span
v - if = "alert.actors.length > 0"
2024-05-28 03:49:28 +00:00
class = "alert-detail alert-actors"
: class = "{ and: alert.and.actors, or: !alert.and.actors }"
>
< span class = "alert-values" > with
< span
v - for = "actor in alert.actors"
: key = "`actor-${alert.id}-${actor.id}`"
class = "alert-key"
>
< a
: href = "`/actor/${actor.id}/${actor.slug}`"
class = "alert-value link"
> { { actor . name } } < / a >
< / span >
< / span >
< / span >
2024-05-27 01:06:54 +00:00
< span
v - if = "alert.entities.length > 0"
2024-05-28 03:49:28 +00:00
class = "alert-detail alert-entities or"
>
< span class = "alert-values" > for
< span
v - for = "entity in alert.entities"
: key = "`entity-${alert.id}-${entity.id}`"
class = "alert-key"
>
< a
: href = "`/${entity.type}/${entity.slug}`"
class = "alert-value link"
>
< Icon
v - if = "entity.type === 'network'"
icon = "device_hub"
/ > { { e n t i t y . n a m e } }
< / a >
< / span >
< / span >
< / span >
2024-05-27 01:06:54 +00:00
< span
v - if = "alert.matches.length > 0"
2024-05-28 03:49:28 +00:00
class = "alert-detail alert-matches"
: class = "{ and: alert.and.matches, or: !alert.and.matches }"
>
< span class = "alert-values" > matching
< span
v - for = "match in alert.matches"
: key = "`match-${alert.id}-${match.id}`"
class = "alert-key"
>
< span class = "alert-value" > { { match . property } } :
< span
class = "alert-regex"
title = "If your original expression was not a /regular expression/, it was converted, and new characters may have been added for syntactical purposes. These characters do not alter the function of the expression; they ensure it."
> { { match . expression } } < / span >
< / span >
< / span >
< / span >
< / span >
2024-05-27 01:06:54 +00:00
< / div >
< div class = "alert-actions" >
2024-05-28 03:49:28 +00:00
< span
class = "alert-id"
title = "Alert ID"
> # { { alert . id } } < / span >
2024-05-27 01:06:54 +00:00
< Icon
icon = "bin"
@ click = "removeAlert(alert)"
/ >
< / div >
< / li >
< / ul >
2024-05-19 03:07:35 +00:00
< AlertDialog
v - if = "showAlertDialog"
@ close = "showAlertDialog = false; reloadAlerts();"
/ >
< / section >
2024-03-26 03:14:42 +00:00
< / div >
2024-02-29 04:08:54 +00:00
< / div >
< / template >
< script setup >
2024-03-26 02:00:50 +00:00
import { ref , inject } from 'vue' ;
2024-03-26 03:14:42 +00:00
import { formatDistanceStrict } from 'date-fns' ;
2024-03-26 02:00:50 +00:00
2024-05-19 03:07:35 +00:00
import { get , post , del } from '#/src/api.js' ;
2024-02-29 04:08:54 +00:00
2024-03-26 23:06:03 +00:00
import StashTile from '#/components/stashes/tile.vue' ;
2024-03-26 03:14:42 +00:00
import Dialog from '#/components/dialog/dialog.vue' ;
2024-05-19 03:07:35 +00:00
import AlertDialog from '#/components/alerts/create.vue' ;
2024-03-26 03:14:42 +00:00
2024-02-29 04:08:54 +00:00
const pageContext = inject ( 'pageContext' ) ;
2024-04-02 03:55:53 +00:00
const user = pageContext . user ;
2024-03-26 02:00:50 +00:00
const profile = ref ( pageContext . pageProps . profile ) ;
2024-05-19 03:07:35 +00:00
const alerts = ref ( pageContext . pageProps . alerts ) ;
2024-03-26 03:14:42 +00:00
const stashName = ref ( null ) ;
const stashNameInput = ref ( null ) ;
2024-03-26 02:00:50 +00:00
const done = ref ( true ) ;
2024-03-26 03:14:42 +00:00
const showStashDialog = ref ( false ) ;
2024-05-19 03:07:35 +00:00
const showAlertDialog = ref ( false ) ;
2024-03-03 01:33:35 +00:00
2024-03-26 23:06:03 +00:00
async function reloadProfile ( ) {
profile . value = await get ( ` /users/ ${ profile . value . id } ` ) ;
2024-03-03 01:33:35 +00:00
}
2024-03-26 02:00:50 +00:00
2024-03-26 03:14:42 +00:00
async function createStash ( ) {
if ( done . value === false ) {
return ;
}
done . value = false ;
2024-05-19 03:07:35 +00:00
try {
await post ( '/stashes' , {
name : stashName . value ,
public : false ,
} , {
successFeedback : ` Created stash ' ${ stashName . value } ' ` ,
errorFeedback : ` Failed to create stash ' ${ stashName . value } ' ` ,
appendErrorMessage : true ,
} ) ;
} finally {
done . value = true ;
}
2024-03-26 03:14:42 +00:00
2024-03-26 23:06:03 +00:00
showStashDialog . value = false ;
stashName . value = null ;
2024-03-26 03:14:42 +00:00
2024-03-26 23:06:03 +00:00
await reloadProfile ( ) ;
2024-03-26 02:00:50 +00:00
}
2024-05-19 03:07:35 +00:00
async function reloadAlerts ( ) {
alerts . value = await get ( '/alerts' ) ;
}
async function removeAlert ( alert ) {
if ( done . value === false ) {
return ;
}
done . value = false ;
const alertLabel = [
... alert . actors . map ( ( actor ) => actor . name ) ,
... alert . tags . map ( ( tag ) => tag . name ) ,
... alert . entities . map ( ( entity ) => entity . name ) ,
... alert . matches . map ( ( match ) => match . expression ) ,
] . filter ( Boolean ) . join ( ', ' ) ;
try {
await del ( ` /alerts/ ${ alert . id } ` , {
undoFeedback : ` Removed alert for ' ${ alertLabel } ' ` ,
errorFeedback : ` Failed to remove alert for ' ${ alertLabel } ' ` ,
appendErrorMessage : true ,
} ) ;
await reloadAlerts ( ) ;
} finally {
done . value = true ;
}
}
2024-02-29 04:08:54 +00:00
< / script >
< style scoped >
2024-03-26 03:14:42 +00:00
. page {
display : flex ;
flex - grow : 1 ;
justify - content : center ;
background : var ( -- background - base - 10 ) ;
}
. profile {
width : 1200 px ;
max - width : 100 % ;
}
2024-02-29 04:08:54 +00:00
. profile - header {
display : flex ;
2024-03-26 03:14:42 +00:00
justify - content : space - between ;
2024-02-29 04:08:54 +00:00
align - items : center ;
padding : .5 rem 1 rem ;
color : var ( -- highlight - strong - 30 ) ;
2024-06-10 01:24:48 +00:00
background : var ( -- shadow - strong - 30 ) ;
2024-03-26 03:14:42 +00:00
border - radius : 0 0 .5 rem .5 rem ;
}
. user {
display : flex ;
overflow : hidden ;
2024-02-29 04:08:54 +00:00
}
. username {
margin : 0 ;
2024-03-26 02:00:50 +00:00
font - size : 1.25 rem ;
2024-02-29 04:08:54 +00:00
}
2024-03-26 03:14:42 +00:00
. age {
display : flex ;
flex - shrink : 0 ;
2024-03-26 23:06:03 +00:00
font - size : .9 rem ;
2024-03-26 03:14:42 +00:00
. icon {
2024-03-26 23:06:03 +00:00
width : .9 rem ;
2024-03-26 03:14:42 +00:00
fill : var ( -- highlight - strong - 20 ) ;
margin - right : .75 rem ;
transform : translateY ( - 1 px ) ;
}
}
2024-02-29 04:08:54 +00:00
. avatar {
2024-03-26 02:00:50 +00:00
width : 1.5 rem ;
height : 1.5 rem ;
2024-02-29 04:08:54 +00:00
border - radius : .25 rem ;
margin - right : 1 rem ;
}
2024-03-03 01:33:35 +00:00
2024-03-26 03:14:42 +00:00
. section - header {
display : flex ;
align - items : center ;
justify - content : space - between ;
padding : .5 rem .5 rem .5 rem .5 rem ;
. button {
margin - left : 1 rem ;
}
}
. heading {
margin : 0 ;
font - size : 1.1 rem ;
color : var ( -- primary ) ;
}
2024-03-03 01:33:35 +00:00
. stashes {
display : grid ;
grid - template - columns : repeat ( auto - fill , minmax ( 12 rem , 1 fr ) ) ;
2024-03-17 04:27:43 +00:00
gap : 1 rem ;
2024-03-26 03:14:42 +00:00
padding : 0 .5 rem 1 rem .5 rem ;
2024-03-03 01:33:35 +00:00
}
2024-05-19 03:07:35 +00:00
. alerts {
width : 100 % ;
}
. alert {
2024-05-28 03:49:28 +00:00
padding : 0 0 0 .5 rem ;
2024-05-27 01:06:54 +00:00
display : flex ;
align - items : stretch ;
2024-06-10 01:24:48 +00:00
border - bottom : solid 1 px var ( -- glass - weak - 40 ) ;
2024-05-27 01:06:54 +00:00
2024-05-28 03:49:28 +00:00
& : hover {
2024-06-10 01:24:48 +00:00
border - color : var ( -- glass - weak - 30 ) ;
2024-05-19 03:07:35 +00:00
}
2024-05-28 03:49:28 +00:00
}
2024-05-19 03:07:35 +00:00
2024-05-28 03:49:28 +00:00
. alert - triggers {
display : flex ;
align - items : center ;
gap : .5 rem ;
2024-05-28 03:55:14 +00:00
margin - right : .75 rem ;
2024-05-28 03:49:28 +00:00
. icon {
2024-06-10 01:24:48 +00:00
fill : var ( -- glass - weak - 40 ) ;
2024-05-19 03:07:35 +00:00
}
2024-05-27 01:12:38 +00:00
2024-05-28 03:49:28 +00:00
. icon . trigger {
2024-06-10 01:24:48 +00:00
fill : var ( -- glass - weak - 10 ) ;
2024-05-27 01:12:38 +00:00
}
2024-05-19 03:07:35 +00:00
}
2024-05-27 01:06:54 +00:00
. alert - details {
2024-05-28 20:18:38 +00:00
padding : .25 rem 0 ;
2024-05-27 01:06:54 +00:00
flex - grow : 1 ;
2024-05-28 20:18:38 +00:00
line - height : 2.5 ;
2024-06-10 01:24:48 +00:00
color : var ( -- glass ) ;
2024-05-19 03:07:35 +00:00
2024-05-28 03:49:28 +00:00
& . and . alert - detail : not ( : last - child ) : after {
content : ' and ' ;
}
& . or . alert - detail : not ( : last - child ) : after {
content : ' or ' ;
}
}
. alert - value {
color : var ( -- text ) ;
. icon {
margin - right : .25 rem ;
2024-06-10 01:24:48 +00:00
fill : var ( -- glass ) ;
2024-05-28 03:49:28 +00:00
transform : translateY ( 2 px ) ;
}
}
. alert - values {
padding : .5 rem .5 rem ;
border - bottom : solid 1 px var ( -- primary - light - 20 ) ;
border - radius : .3 rem ;
}
. alert - detail {
& . and . alert - key : not ( : last - child ) : after {
content : ' and ' ;
}
& . or . alert - key : not ( : last - child ) : after {
content : ' or ' ;
}
}
. alert - regex {
& : before ,
& : after {
content : '╱ ' ;
padding : 0 .1 rem ;
color : var ( -- primary - light - 20 ) ;
2024-05-27 01:06:54 +00:00
}
2024-05-19 03:07:35 +00:00
}
. alert - actions {
2024-05-28 03:49:28 +00:00
display : flex ;
align - items : center ;
font - size : .9 rem ;
2024-06-10 01:24:48 +00:00
color : var ( -- glass - weak - 10 ) ;
2024-05-28 03:49:28 +00:00
2024-05-19 03:07:35 +00:00
. icon {
height : 100 % ;
2024-05-28 03:49:28 +00:00
padding : 0 .75 rem ;
2024-06-10 01:24:48 +00:00
fill : var ( -- glass ) ;
2024-05-19 03:07:35 +00:00
& : hover {
cursor : pointer ;
fill : var ( -- primary ) ;
}
}
}
2024-03-26 03:14:42 +00:00
. dialog - body {
padding : 1 rem ;
. input {
margin - bottom : .5 rem ;
}
}
@ media ( -- compact ) {
. profile - header {
border - radius : 0 ;
}
. section - header {
padding : .5 rem 1 rem .5 rem 1 rem ;
}
. stashes {
padding : 0 1 rem 1 rem 1 rem ;
}
2024-05-28 03:49:28 +00:00
. alert {
padding : 0 .5 rem 0 1 rem ;
}
2024-03-26 03:14:42 +00:00
}
@ media ( -- small - 30 ) {
. section - header {
padding : .5 rem .5 rem .5 rem .5 rem ;
}
. stashes {
padding : 0 .5 rem 1 rem .5 rem ;
}
. age . icon {
display : none ;
}
}
@ media ( -- small - 50 ) {
. age {
display : none ;
}
}
2024-02-29 04:08:54 +00:00
< / style >