Some search box styling, mock trends

This commit is contained in:
styppo 2022-12-24 00:31:31 +00:00
parent 028e870f5f
commit bcde6aa0b3
No known key found for this signature in database
GPG Key ID: 3AAA685C50724C28
10 changed files with 188 additions and 68 deletions

View File

@ -54,10 +54,6 @@ export default defineComponent({
display: flex;
align-items: center;
h2 {
font-size: 1.5em;
font-weight: bold;
line-height: unset;
letter-spacing: unset;
margin: .5rem 0;
}
.back-button {

View File

@ -159,6 +159,7 @@ export default {
&-content {
margin-left: 12px;
flex-grow: 1;
max-width: 570px;
&-header {
.in-reply-to {
color: $color-dark-gray;

View File

@ -8,34 +8,149 @@
<BaseIcon icon="search" />
</div>
<div class="searchbox-input">
<form @submit="searchProfile">
<input
type="text"
placeholder="Search Profiles"
placeholder="Search Hamstr"
v-model="query"
@focus="toggleFocus"
@blur="toggleFocus"
>
</form>
</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>
<script>
import BaseIcon from 'components/BaseIcon'
import {Notify} from 'quasar'
import {searchDomain, queryName} from 'nostr-tools/nip05'
import helpersMixin from 'src/utils/mixin'
export default {
name: 'SearchBar',
name: 'SearchBox',
components: {
BaseIcon,
},
mixins: [helpersMixin],
data() {
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: {
toggleFocus() {
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>

View File

@ -97,7 +97,7 @@
</template>
</q-input>
</q-card-section>
<!-- <div v-if='isBeck32Key(key)'>
<!-- <div v-if='isBech32Key(key)'>
{{ hexKey }}
</div> -->
</q-form>
@ -234,14 +234,14 @@ export default defineComponent({
// npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s
// nsec1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzs46ahj9
// 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
if (this.isBeck32Key(this.key)) {
return this.beck32ToHex(this.key)
if (this.isBech32Key(this.key)) {
return this.bech32ToHex(this.key)
}
return this.key?.toLowerCase()
},
isBech32Pub() {
if (this.isBeck32Key(this.key)) {
if (this.isBech32Key(this.key)) {
let { prefix } = decode(this.key.toLowerCase())
return prefix === 'npub'
}
@ -249,7 +249,7 @@ export default defineComponent({
},
isBech32Sec() {
if (this.isBeck32Key(this.key)) {
if (this.isBech32Key(this.key)) {
let { prefix } = decode(this.key.toLowerCase())
return prefix === 'nsec'
}

View File

@ -132,7 +132,7 @@ export default defineComponent({
validSearch() {
if (this.searchingProfile === '') 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
return false
},
@ -174,8 +174,8 @@ export default defineComponent({
return
}
if (this.isBeck32Key(this.searchingProfile) && this.beck32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) {
this.toProfile(this.beck32ToHex(this.searchingProfile))
if (this.isBech32Key(this.searchingProfile) && this.bech32ToHex(this.searchingProfile).match(/^[a-f0-9A-F]{64}$/)) {
this.toProfile(this.bech32ToHex(this.searchingProfile))
this.searchingProfile = ''
this.searching = false
return

View File

@ -1,7 +1,7 @@
<template>
<div class="trends-item">
<h3>{{ data.name }}</h3>
<span>{{ normalizedTweetCount }} Tweets</span>
<span>{{ nicePostCount }} posts</span>
</div>
</template>
@ -15,10 +15,10 @@ export default {
}
},
computed: {
normalizedTweetCount(){
const stringNumber = this.data.tweetsCount.toString();
nicePostCount() {
const stringNumber = this.data.postCount.toString()
if (stringNumber.length > 4) {
return stringNumber.substring(0, stringNumber.length - 3) + "K"
return stringNumber.substring(0, stringNumber.length - 3) + 'K'
}
return stringNumber
}
@ -27,7 +27,8 @@ export default {
</script>
<style lang="scss">
@import '@/assets/theme/colors.scss';
@import 'assets/theme/colors.scss';
.trends-item {
padding: 1rem;
border-top: $border-dark;

View File

@ -19,8 +19,7 @@
</template>
<script>
import TrendsItem from '@/components/Trends/Item'
import { getTrends } from '@/services/api'
import TrendsItem from 'components/Trends/Item'
export default {
name: 'Trends',
@ -29,33 +28,27 @@ export default {
},
data() {
return {
trends: []
trends: [
{
name: '#nostr',
postCount: 58
}
]
}
},
computed: {
sortedTrends() {
const trendsArray = this.trends;
trendsArray.sort((a,b) => a.tweetsCount > b.tweetsCount ? -1 : 1, 0)
const trendsArray = this.trends
trendsArray.sort((a, b) => a.postCount > b.postCount ? -1 : 1)
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>
<style lang="scss">
@import '@/assets/theme/colors.scss';
@import 'assets/theme/colors.scss';
.trends {
background-color: rgba($color: $color-dark-gray, $alpha: 0.1);
border-radius: 1rem;
@ -70,7 +63,6 @@ export default {
}
}
&-body {
}
}
</style>

View File

@ -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;
}

View File

@ -42,7 +42,8 @@
<div class="layout-sidebar">
<div class="layout-sidebar-fixed">
<TheSearchMenu/>
<search-box />
<trends />
</div>
</div>
</div>
@ -90,16 +91,18 @@ const { getVerticalScrollPosition, setVerticalScrollPosition} = scroll
import { activateSub, deactivateSub, destroyStreams } from '../query'
import MainMenu from 'components/MainMenu/index.vue'
import TheUserMenu from 'components/TheUserMenu.vue'
import TheSearchMenu from 'components/TheSearchMenu.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'
export default defineComponent({
name: 'MainLayout',
components: {
MainMenu,
SearchBox,
Trends,
TheUserMenu,
TheSearchMenu,
TheKeyInitializationDialog,
},
@ -338,7 +341,7 @@ export default defineComponent({
</script>
<style lang='scss'>
<style lang="scss">
@import 'assets/theme/colors.scss';
@import 'assets/variables.scss';

View File

@ -287,21 +287,21 @@ export default {
return false
},
isBeck32Key(key) {
isBech32Key(key) {
if (typeof key !== 'string') return false
try {
let { prefix } = decode(key.toLowerCase())
if (!['npub', 'nsec'].includes(prefix)) return false
if (prefix === 'npub') this.watchOnly = true
if (prefix === 'nsec') this.watchOnly = false
if (!this.isKey(this.beck32ToHex(key))) return false
if (!this.isKey(this.bech32ToHex(key))) return false
} catch (error) {
return false
}
return true
},
beck32ToHex(key) {
bech32ToHex(key) {
let { data } = decode(key.toLowerCase())
return data.reduce((s, byte) => {
let hex = byte.toString(16)