mirror of
https://github.com/styppo/hamstr.git
synced 2024-10-18 13:33:22 +00:00
Some search box styling, mock trends
This commit is contained in:
parent
028e870f5f
commit
bcde6aa0b3
@ -54,10 +54,6 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: unset;
|
|
||||||
letter-spacing: unset;
|
|
||||||
margin: .5rem 0;
|
margin: .5rem 0;
|
||||||
}
|
}
|
||||||
.back-button {
|
.back-button {
|
||||||
|
@ -159,6 +159,7 @@ export default {
|
|||||||
&-content {
|
&-content {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
max-width: 570px;
|
||||||
&-header {
|
&-header {
|
||||||
.in-reply-to {
|
.in-reply-to {
|
||||||
color: $color-dark-gray;
|
color: $color-dark-gray;
|
||||||
|
@ -8,34 +8,149 @@
|
|||||||
<BaseIcon icon="search" />
|
<BaseIcon icon="search" />
|
||||||
</div>
|
</div>
|
||||||
<div class="searchbox-input">
|
<div class="searchbox-input">
|
||||||
|
<form @submit="searchProfile">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Profiles"
|
placeholder="Search Hamstr"
|
||||||
|
v-model="query"
|
||||||
@focus="toggleFocus"
|
@focus="toggleFocus"
|
||||||
@blur="toggleFocus"
|
@blur="toggleFocus"
|
||||||
>
|
>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="domainMode">
|
||||||
|
<div class="flex row justify-between no-wrap">
|
||||||
|
<h2 class="text-h6 text-bold q-my-none"> {{ domain }} {{ $t('users') }}</h2>
|
||||||
|
<q-btn icon="close" @click.stop="domainMode = false" />
|
||||||
|
</div>
|
||||||
|
<div v-if="domainDefaultPubkey">
|
||||||
|
<h2 class="text-caption text-bold q-my-none"> {{ $t('nip05Maintainer') }} </h2>
|
||||||
|
<BaseUserCard :pubkey="domainDefaultPubkey"/>
|
||||||
|
</div>
|
||||||
|
<q-list class="q-pt-xs q-pl-sm" style="overflow-y: auto; max-height: 40vh;">
|
||||||
|
<div v-for="user in domainUsers" :key="user.pubkey">
|
||||||
|
<BaseUserCard :pubkey="user.pubkey" />
|
||||||
|
</div>
|
||||||
|
</q-list>
|
||||||
|
<q-separator color='accent' />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseIcon from 'components/BaseIcon'
|
import BaseIcon from 'components/BaseIcon'
|
||||||
|
import {Notify} from 'quasar'
|
||||||
|
import {searchDomain, queryName} from 'nostr-tools/nip05'
|
||||||
|
import helpersMixin from 'src/utils/mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SearchBar',
|
name: 'SearchBox',
|
||||||
components: {
|
components: {
|
||||||
BaseIcon,
|
BaseIcon,
|
||||||
},
|
},
|
||||||
|
mixins: [helpersMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isFocused: false
|
isFocused: false,
|
||||||
|
query: '',
|
||||||
|
searching: false,
|
||||||
|
domainMode: false,
|
||||||
|
domainNames: {},
|
||||||
|
profilesUsed: new Set(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validSearch() {
|
||||||
|
if (this.query === '') return true
|
||||||
|
if (this.query.match(/^[a-f0-9A-F]{64}$/)) return true
|
||||||
|
if (this.isBech32Key(this.query) && this.bech32ToHex(this.query).match(/^[a-f0-9A-F]{64}$/)) return true
|
||||||
|
if (this.query.match(/^([a-z0-9A-Z-_.\u00C0-\u1FFF\u2800-\uFFFD]*@)?[a-z0-9A-Z-_]+[.]{1}[a-z0-9A-Z-_.]+$/)) return true
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
domainDefaultPubkey() {
|
||||||
|
return this.domainNames._
|
||||||
|
},
|
||||||
|
domainUsers() {
|
||||||
|
let users = Object.keys(this.domainNames).filter((name) => name !== '_').map((name) => { return { 'name': name, 'pubkey': this.domainNames[name] } })
|
||||||
|
return users
|
||||||
|
},
|
||||||
|
domain() {
|
||||||
|
let [name, domain] = this.query.split('@')
|
||||||
|
return domain || name
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleFocus() {
|
toggleFocus() {
|
||||||
this.isFocused = !this.isFocused
|
this.isFocused = !this.isFocused
|
||||||
|
},
|
||||||
|
async searchProfile(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (!this.validSearch) {
|
||||||
|
Notify.create({
|
||||||
|
message: 'Invalid format! Please enter full public key or NIP05 identifier',
|
||||||
|
color: 'negative'
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.searching = true
|
||||||
|
this.query = this.query.trim().toLowerCase()
|
||||||
|
|
||||||
|
if (this.query.match(/^[a-f0-9]{64}$/)) {
|
||||||
|
this.toProfile(this.query)
|
||||||
|
this.query = ''
|
||||||
|
this.searching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isBech32Key(this.query) && this.bech32ToHex(this.query).match(/^[a-f0-9A-F]{64}$/)) {
|
||||||
|
this.toProfile(this.bech32ToHex(this.query))
|
||||||
|
this.query = ''
|
||||||
|
this.searching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.query.match(/^([a-z0-9-_.\u00C0-\u1FFF\u2800-\uFFFD]*@)?[a-z0-9-_.]+[.]{1}[a-z0-9-_.]+$/)) {
|
||||||
|
// if (!this.query.match(/^[a-z0-9-_.\u00C0-\u1FFF\u2800-\uFFFD]?@/)) {
|
||||||
|
if (this.query.match(/^@/) || !this.query.match(/@/)) {
|
||||||
|
// this.query = '_' + this.query
|
||||||
|
// else if (!this.query.match(/@/)) this.query = '_@' + this.query
|
||||||
|
this.domainNames = await searchDomain(this.domain)
|
||||||
|
// this.domainUsers
|
||||||
|
if (this.domainUsers.length || this.domainDefaultPubkey) {
|
||||||
|
if (this.domainDefaultPubkey) this.useProfile(this.domainDefaultPubkey)
|
||||||
|
if (this.domainUsers.length) this.domainUsers.forEach((user) => this.useProfile(user.pubkey))
|
||||||
|
this.searching = false
|
||||||
|
this.domainMode = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
console.log('this.domainUsers', this.domainUsers)
|
||||||
|
let pubkey = await queryName(this.query)
|
||||||
|
console.log('queryName returned: ', pubkey)
|
||||||
|
if (pubkey) {
|
||||||
|
this.toProfile(pubkey)
|
||||||
|
this.query = ''
|
||||||
|
this.searching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.searching = false
|
||||||
|
Notify.create({
|
||||||
|
message: 'No user found! Please enter full public key or NIP05 identifier and double check search string',
|
||||||
|
color: 'negative'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
useProfile(pubkey) {
|
||||||
|
if (this.profilesUsed.has(pubkey)) return
|
||||||
|
|
||||||
|
this.profilesUsed.add(pubkey)
|
||||||
|
this.$store.dispatch('useProfile', {pubkey})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<!-- <div v-if='isBeck32Key(key)'>
|
<!-- <div v-if='isBech32Key(key)'>
|
||||||
{{ hexKey }}
|
{{ hexKey }}
|
||||||
</div> -->
|
</div> -->
|
||||||
</q-form>
|
</q-form>
|
||||||
@ -234,14 +234,14 @@ export default defineComponent({
|
|||||||
// npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s
|
// npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s
|
||||||
// nsec1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzs46ahj9
|
// nsec1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzs46ahj9
|
||||||
// 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
|
// 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
|
||||||
if (this.isBeck32Key(this.key)) {
|
if (this.isBech32Key(this.key)) {
|
||||||
return this.beck32ToHex(this.key)
|
return this.bech32ToHex(this.key)
|
||||||
}
|
}
|
||||||
return this.key?.toLowerCase()
|
return this.key?.toLowerCase()
|
||||||
},
|
},
|
||||||
|
|
||||||
isBech32Pub() {
|
isBech32Pub() {
|
||||||
if (this.isBeck32Key(this.key)) {
|
if (this.isBech32Key(this.key)) {
|
||||||
let { prefix } = decode(this.key.toLowerCase())
|
let { prefix } = decode(this.key.toLowerCase())
|
||||||
return prefix === 'npub'
|
return prefix === 'npub'
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
isBech32Sec() {
|
isBech32Sec() {
|
||||||
if (this.isBeck32Key(this.key)) {
|
if (this.isBech32Key(this.key)) {
|
||||||
let { prefix } = decode(this.key.toLowerCase())
|
let { prefix } = decode(this.key.toLowerCase())
|
||||||
return prefix === 'nsec'
|
return prefix === 'nsec'
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ export default defineComponent({
|
|||||||
validSearch() {
|
validSearch() {
|
||||||
if (this.searchingProfile === '') return true
|
if (this.searchingProfile === '') return true
|
||||||
if (this.searchingProfile.match(/^[a-f0-9A-F]{64}$/)) return true
|
if (this.searchingProfile.match(/^[a-f0-9A-F]{64}$/)) return true
|
||||||
if (this.isBeck32Key(this.searchingProfile) && this.beck32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) return true
|
if (this.isBech32Key(this.searchingProfile) && this.bech32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) return true
|
||||||
if (this.searchingProfile.match(/^([a-z0-9A-Z-_.\u00C0-\u1FFF\u2800-\uFFFD]*@)?[a-z0-9A-Z-_]+[.]{1}[a-z0-9A-Z-_.]+$/)) return true
|
if (this.searchingProfile.match(/^([a-z0-9A-Z-_.\u00C0-\u1FFF\u2800-\uFFFD]*@)?[a-z0-9A-Z-_]+[.]{1}[a-z0-9A-Z-_.]+$/)) return true
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
@ -174,8 +174,8 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isBeck32Key(this.searchingProfile) && this.beck32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) {
|
if (this.isBech32Key(this.searchingProfile) && this.bech32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) {
|
||||||
this.toProfile(this.beck32ToHex(this.searchingProfile))
|
this.toProfile(this.bech32ToHex(this.searchingProfile))
|
||||||
this.searchingProfile = ''
|
this.searchingProfile = ''
|
||||||
this.searching = false
|
this.searching = false
|
||||||
return
|
return
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="trends-item">
|
<div class="trends-item">
|
||||||
<h3>{{ data.name }}</h3>
|
<h3>{{ data.name }}</h3>
|
||||||
<span>{{ normalizedTweetCount }} Tweets</span>
|
<span>{{ nicePostCount }} posts</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,10 +15,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
normalizedTweetCount(){
|
nicePostCount() {
|
||||||
const stringNumber = this.data.tweetsCount.toString();
|
const stringNumber = this.data.postCount.toString()
|
||||||
if(stringNumber.length > 4){
|
if (stringNumber.length > 4) {
|
||||||
return stringNumber.substring(0, stringNumber.length - 3) + "K"
|
return stringNumber.substring(0, stringNumber.length - 3) + 'K'
|
||||||
}
|
}
|
||||||
return stringNumber
|
return stringNumber
|
||||||
}
|
}
|
||||||
@ -27,15 +27,16 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/assets/theme/colors.scss';
|
@import 'assets/theme/colors.scss';
|
||||||
.trends-item{
|
|
||||||
|
.trends-item {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-top: $border-dark;
|
border-top: $border-dark;
|
||||||
h3{
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
span{
|
span {
|
||||||
color: $color-dark-gray;
|
color: $color-dark-gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,58 +19,50 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TrendsItem from '@/components/Trends/Item'
|
import TrendsItem from 'components/Trends/Item'
|
||||||
import { getTrends } from '@/services/api'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Trends',
|
name: 'Trends',
|
||||||
components:{
|
components: {
|
||||||
TrendsItem,
|
TrendsItem,
|
||||||
},
|
},
|
||||||
data(){
|
data() {
|
||||||
return{
|
return {
|
||||||
trends: []
|
trends: [
|
||||||
|
{
|
||||||
|
name: '#nostr',
|
||||||
|
postCount: 58
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sortedTrends(){
|
sortedTrends() {
|
||||||
const trendsArray = this.trends;
|
const trendsArray = this.trends
|
||||||
trendsArray.sort((a,b) => a.tweetsCount > b.tweetsCount ? -1 : 1, 0)
|
trendsArray.sort((a, b) => a.postCount > b.postCount ? -1 : 1)
|
||||||
return trendsArray
|
return trendsArray
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted(){
|
|
||||||
try{
|
|
||||||
const response = await getTrends();
|
|
||||||
const trends = response.data.trends;
|
|
||||||
this.trends = trends;
|
|
||||||
} catch(err){
|
|
||||||
this.$notification({
|
|
||||||
type: 'error',
|
|
||||||
message: 'Error when fetching trends'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/assets/theme/colors.scss';
|
@import 'assets/theme/colors.scss';
|
||||||
.trends{
|
|
||||||
|
.trends {
|
||||||
background-color: rgba($color: $color-dark-gray, $alpha: 0.1);
|
background-color: rgba($color: $color-dark-gray, $alpha: 0.1);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
&-wrapper{
|
&-wrapper {
|
||||||
}
|
}
|
||||||
&-header{
|
&-header {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
h3{
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-body{
|
&-body {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1 +1,13 @@
|
|||||||
// app global css in SCSS form
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: unset;
|
||||||
|
letter-spacing: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: unset;
|
||||||
|
letter-spacing: unset;
|
||||||
|
}
|
||||||
|
@ -42,7 +42,8 @@
|
|||||||
|
|
||||||
<div class="layout-sidebar">
|
<div class="layout-sidebar">
|
||||||
<div class="layout-sidebar-fixed">
|
<div class="layout-sidebar-fixed">
|
||||||
<TheSearchMenu/>
|
<search-box />
|
||||||
|
<trends />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -90,16 +91,18 @@ const { getVerticalScrollPosition, setVerticalScrollPosition} = scroll
|
|||||||
import { activateSub, deactivateSub, destroyStreams } from '../query'
|
import { activateSub, deactivateSub, destroyStreams } from '../query'
|
||||||
import MainMenu from 'components/MainMenu/index.vue'
|
import MainMenu from 'components/MainMenu/index.vue'
|
||||||
import TheUserMenu from 'components/TheUserMenu.vue'
|
import TheUserMenu from 'components/TheUserMenu.vue'
|
||||||
import TheSearchMenu from 'components/TheSearchMenu.vue'
|
|
||||||
import TheKeyInitializationDialog from 'components/TheKeyInitializationDialog.vue'
|
import TheKeyInitializationDialog from 'components/TheKeyInitializationDialog.vue'
|
||||||
|
import SearchBox from 'components/SearchBox/index.vue'
|
||||||
|
import Trends from 'components/Trends/index.vue'
|
||||||
import { setCssVar, getCssVar } from 'quasar'
|
import { setCssVar, getCssVar } from 'quasar'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MainLayout',
|
name: 'MainLayout',
|
||||||
components: {
|
components: {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
|
SearchBox,
|
||||||
|
Trends,
|
||||||
TheUserMenu,
|
TheUserMenu,
|
||||||
TheSearchMenu,
|
|
||||||
TheKeyInitializationDialog,
|
TheKeyInitializationDialog,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -338,7 +341,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang="scss">
|
||||||
@import 'assets/theme/colors.scss';
|
@import 'assets/theme/colors.scss';
|
||||||
@import 'assets/variables.scss';
|
@import 'assets/variables.scss';
|
||||||
|
|
||||||
|
@ -287,21 +287,21 @@ export default {
|
|||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
isBeck32Key(key) {
|
isBech32Key(key) {
|
||||||
if (typeof key !== 'string') return false
|
if (typeof key !== 'string') return false
|
||||||
try {
|
try {
|
||||||
let { prefix } = decode(key.toLowerCase())
|
let { prefix } = decode(key.toLowerCase())
|
||||||
if (!['npub', 'nsec'].includes(prefix)) return false
|
if (!['npub', 'nsec'].includes(prefix)) return false
|
||||||
if (prefix === 'npub') this.watchOnly = true
|
if (prefix === 'npub') this.watchOnly = true
|
||||||
if (prefix === 'nsec') this.watchOnly = false
|
if (prefix === 'nsec') this.watchOnly = false
|
||||||
if (!this.isKey(this.beck32ToHex(key))) return false
|
if (!this.isKey(this.bech32ToHex(key))) return false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
beck32ToHex(key) {
|
bech32ToHex(key) {
|
||||||
let { data } = decode(key.toLowerCase())
|
let { data } = decode(key.toLowerCase())
|
||||||
return data.reduce((s, byte) => {
|
return data.reduce((s, byte) => {
|
||||||
let hex = byte.toString(16)
|
let hex = byte.toString(16)
|
||||||
|
Loading…
Reference in New Issue
Block a user