Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
pleroma-fe
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Joseph Nuthalapati
pleroma-fe
Commits
e9b45257
Commit
e9b45257
authored
Jul 15, 2019
by
Shpuld Shpludson
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'new-search' into 'develop'
New search See merge request
pleroma/pleroma-fe!832
parents
0c064105
69a4bcb2
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
451 additions
and
153 deletions
+451
-153
src/App.js
src/App.js
+6
-6
src/App.vue
src/App.vue
+3
-3
src/boot/routes.js
src/boot/routes.js
+2
-2
src/components/search/search.js
src/components/search/search.js
+98
-0
src/components/search/search.vue
src/components/search/search.vue
+211
-0
src/components/search_bar/search_bar.js
src/components/search_bar/search_bar.js
+27
-0
src/components/search_bar/search_bar.vue
src/components/search_bar/search_bar.vue
+22
-16
src/components/side_drawer/side_drawer.vue
src/components/side_drawer/side_drawer.vue
+2
-2
src/components/tab_switcher/tab_switcher.js
src/components/tab_switcher/tab_switcher.js
+10
-2
src/components/user_search/user_search.js
src/components/user_search/user_search.js
+0
-49
src/components/user_search/user_search.vue
src/components/user_search/user_search.vue
+0
-57
src/i18n/en.json
src/i18n/en.json
+8
-0
src/i18n/ru.json
src/i18n/ru.json
+9
-1
src/modules/statuses.js
src/modules/statuses.js
+8
-0
src/services/api/api.service.js
src/services/api/api.service.js
+42
-12
src/services/backend_interactor_service/backend_interactor_service.js
.../backend_interactor_service/backend_interactor_service.js
+3
-3
No files found.
src/App.js
View file @
e9b45257
import
UserPanel
from
'
./components/user_panel/user_panel.vue
'
import
NavPanel
from
'
./components/nav_panel/nav_panel.vue
'
import
Notifications
from
'
./components/notifications/notifications.vue
'
import
UserFinder
from
'
./components/user_finder/user_finde
r.vue
'
import
SearchBar
from
'
./components/search_bar/search_ba
r.vue
'
import
InstanceSpecificPanel
from
'
./components/instance_specific_panel/instance_specific_panel.vue
'
import
FeaturesPanel
from
'
./components/features_panel/features_panel.vue
'
import
WhoToFollowPanel
from
'
./components/who_to_follow_panel/who_to_follow_panel.vue
'
...
...
@@ -19,7 +19,7 @@ export default {
UserPanel
,
NavPanel
,
Notifications
,
UserFinde
r
,
SearchBa
r
,
InstanceSpecificPanel
,
FeaturesPanel
,
WhoToFollowPanel
,
...
...
@@ -32,7 +32,7 @@ export default {
},
data
:
()
=>
({
mobileActivePanel
:
'
timeline
'
,
finde
rHidden
:
true
,
searchBa
rHidden
:
true
,
supportsMask
:
window
.
CSS
&&
window
.
CSS
.
supports
&&
(
window
.
CSS
.
supports
(
'
mask-size
'
,
'
contain
'
)
||
window
.
CSS
.
supports
(
'
-webkit-mask-size
'
,
'
contain
'
)
||
...
...
@@ -70,7 +70,7 @@ export default {
logoBgStyle
()
{
return
Object
.
assign
({
'
margin
'
:
`
${
this
.
$store
.
state
.
instance
.
logoMargin
}
0
`
,
opacity
:
this
.
finde
rHidden
?
1
:
0
opacity
:
this
.
searchBa
rHidden
?
1
:
0
},
this
.
enableMask
?
{}
:
{
'
background-color
'
:
this
.
enableMask
?
''
:
'
transparent
'
})
...
...
@@ -101,8 +101,8 @@ export default {
this
.
$router
.
replace
(
'
/main/public
'
)
this
.
$store
.
dispatch
(
'
logout
'
)
},
on
Finde
rToggled
(
hidden
)
{
this
.
finde
rHidden
=
hidden
on
SearchBa
rToggled
(
hidden
)
{
this
.
searchBa
rHidden
=
hidden
},
updateMobileState
()
{
const
mobileLayout
=
windowWidth
()
<=
800
...
...
src/App.vue
View file @
e9b45257
...
...
@@ -38,9 +38,9 @@
</router-link>
</div>
<div
class=
"item right"
>
<
user-finde
r
class=
"
button-icon
nav-icon mobile-hidden"
@
toggled=
"on
Finde
rToggled"
<
search-ba
r
class=
"nav-icon mobile-hidden"
@
toggled=
"on
SearchBa
rToggled"
/>
<router-link
class=
"mobile-hidden"
...
...
src/boot/routes.js
View file @
e9b45257
...
...
@@ -6,12 +6,12 @@ import ConversationPage from 'components/conversation-page/conversation-page.vue
import
Interactions
from
'
components/interactions/interactions.vue
'
import
DMs
from
'
components/dm_timeline/dm_timeline.vue
'
import
UserProfile
from
'
components/user_profile/user_profile.vue
'
import
Search
from
'
components/search/search.vue
'
import
Settings
from
'
components/settings/settings.vue
'
import
Registration
from
'
components/registration/registration.vue
'
import
UserSettings
from
'
components/user_settings/user_settings.vue
'
import
FollowRequests
from
'
components/follow_requests/follow_requests.vue
'
import
OAuthCallback
from
'
components/oauth_callback/oauth_callback.vue
'
import
UserSearch
from
'
components/user_search/user_search.vue
'
import
Notifications
from
'
components/notifications/notifications.vue
'
import
AuthForm
from
'
components/auth_form/auth_form.js
'
import
ChatPanel
from
'
components/chat_panel/chat_panel.vue
'
...
...
@@ -45,7 +45,7 @@ export default (store) => {
{
name
:
'
login
'
,
path
:
'
/login
'
,
component
:
AuthForm
},
{
name
:
'
chat
'
,
path
:
'
/chat
'
,
component
:
ChatPanel
,
props
:
()
=>
({
floating
:
false
})
},
{
name
:
'
oauth-callback
'
,
path
:
'
/oauth-callback
'
,
component
:
OAuthCallback
,
props
:
(
route
)
=>
({
code
:
route
.
query
.
code
})
},
{
name
:
'
user-search
'
,
path
:
'
/user-search
'
,
component
:
User
Search
,
props
:
(
route
)
=>
({
query
:
route
.
query
.
query
})
},
{
name
:
'
search
'
,
path
:
'
/search
'
,
component
:
Search
,
props
:
(
route
)
=>
({
query
:
route
.
query
.
query
})
},
{
name
:
'
who-to-follow
'
,
path
:
'
/who-to-follow
'
,
component
:
WhoToFollow
},
{
name
:
'
about
'
,
path
:
'
/about
'
,
component
:
About
},
{
name
:
'
user-profile
'
,
path
:
'
/(users/)?:name
'
,
component
:
UserProfile
}
...
...
src/components/search/search.js
0 → 100644
View file @
e9b45257
import
FollowCard
from
'
../follow_card/follow_card.vue
'
import
Conversation
from
'
../conversation/conversation.vue
'
import
Status
from
'
../status/status.vue
'
import
map
from
'
lodash/map
'
const
Search
=
{
components
:
{
FollowCard
,
Conversation
,
Status
},
props
:
[
'
query
'
],
data
()
{
return
{
loaded
:
false
,
loading
:
false
,
searchTerm
:
this
.
query
||
''
,
userIds
:
[],
statuses
:
[],
hashtags
:
[],
currenResultTab
:
'
statuses
'
}
},
computed
:
{
users
()
{
return
this
.
userIds
.
map
(
userId
=>
this
.
$store
.
getters
.
findUser
(
userId
))
},
visibleStatuses
()
{
const
allStatusesObject
=
this
.
$store
.
state
.
statuses
.
allStatusesObject
return
this
.
statuses
.
filter
(
status
=>
allStatusesObject
[
status
.
id
]
&&
!
allStatusesObject
[
status
.
id
].
deleted
)
}
},
mounted
()
{
this
.
search
(
this
.
query
)
},
watch
:
{
query
(
newValue
)
{
this
.
searchTerm
=
newValue
this
.
search
(
newValue
)
}
},
methods
:
{
newQuery
(
query
)
{
this
.
$router
.
push
({
name
:
'
search
'
,
query
:
{
query
}
})
this
.
$refs
.
searchInput
.
focus
()
},
search
(
query
)
{
if
(
!
query
)
{
this
.
loading
=
false
return
}
this
.
loading
=
true
this
.
userIds
=
[]
this
.
statuses
=
[]
this
.
hashtags
=
[]
this
.
$refs
.
searchInput
.
blur
()
this
.
$store
.
dispatch
(
'
search
'
,
{
q
:
query
,
resolve
:
true
})
.
then
(
data
=>
{
this
.
loading
=
false
this
.
userIds
=
map
(
data
.
accounts
,
'
id
'
)
this
.
statuses
=
data
.
statuses
this
.
hashtags
=
data
.
hashtags
this
.
currenResultTab
=
this
.
getActiveTab
()
this
.
loaded
=
true
})
},
resultCount
(
tabName
)
{
const
length
=
this
[
tabName
].
length
return
length
===
0
?
''
:
`
(
${
length
}
)
`
},
onResultTabSwitch
(
_index
,
dataset
)
{
this
.
currenResultTab
=
dataset
.
filter
},
getActiveTab
()
{
if
(
this
.
visibleStatuses
.
length
>
0
)
{
return
'
statuses
'
}
else
if
(
this
.
users
.
length
>
0
)
{
return
'
people
'
}
else
if
(
this
.
hashtags
.
length
>
0
)
{
return
'
hashtags
'
}
return
'
statuses
'
},
lastHistoryRecord
(
hashtag
)
{
return
hashtag
.
history
&&
hashtag
.
history
[
0
]
}
}
}
export
default
Search
src/components/search/search.vue
0 → 100644
View file @
e9b45257
<
template
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"title"
>
{{
$t
(
'
nav.search
'
)
}}
</div>
</div>
<div
class=
"search-input-container"
>
<input
ref=
"searchInput"
v-model=
"searchTerm"
class=
"search-input"
:placeholder=
"$t('nav.search')"
@
keyup.enter=
"newQuery(searchTerm)"
>
<button
class=
"btn search-button"
@
click=
"newQuery(searchTerm)"
>
<i
class=
"icon-search"
/>
</button>
</div>
<div
v-if=
"loading"
class=
"text-center loading-icon"
>
<i
class=
"icon-spin3 animate-spin"
/>
</div>
<div
v-else-if=
"loaded"
>
<div
class=
"search-nav-heading"
>
<tab-switcher
ref=
"tabSwitcher"
:on-switch=
"onResultTabSwitch"
:custom-active=
"currenResultTab"
>
<span
data-tab-dummy
data-filter=
"statuses"
:label=
"$t('user_card.statuses') + resultCount('visibleStatuses')"
/>
<span
data-tab-dummy
data-filter=
"people"
:label=
"$t('search.people') + resultCount('users')"
/>
<span
data-tab-dummy
data-filter=
"hashtags"
:label=
"$t('search.hashtags') + resultCount('hashtags')"
/>
</tab-switcher>
</div>
</div>
<div
class=
"panel-body"
>
<div
v-if=
"currenResultTab === 'statuses'"
>
<div
v-if=
"visibleStatuses.length === 0 && !loading && loaded"
class=
"search-result-heading"
>
<h4>
{{
$t
(
'
search.no_results
'
)
}}
</h4>
</div>
<Status
v-for=
"status in visibleStatuses"
:key=
"status.id"
:collapsable=
"false"
:expandable=
"false"
:compact=
"false"
class=
"search-result"
:statusoid=
"status"
:no-heading=
"false"
/>
</div>
<div
v-else-if=
"currenResultTab === 'people'"
>
<div
v-if=
"users.length === 0 && !loading && loaded"
class=
"search-result-heading"
>
<h4>
{{
$t
(
'
search.no_results
'
)
}}
</h4>
</div>
<FollowCard
v-for=
"user in users"
:key=
"user.id"
:user=
"user"
class=
"list-item search-result"
/>
</div>
<div
v-else-if=
"currenResultTab === 'hashtags'"
>
<div
v-if=
"hashtags.length === 0 && !loading && loaded"
class=
"search-result-heading"
>
<h4>
{{
$t
(
'
search.no_results
'
)
}}
</h4>
</div>
<div
v-for=
"hashtag in hashtags"
:key=
"hashtag.url"
class=
"status trend search-result"
>
<div
class=
"hashtag"
>
<router-link
:to=
"
{ name: 'tag-timeline', params: { tag: hashtag.name } }">
#
{{
hashtag
.
name
}}
</router-link>
<div
v-if=
"lastHistoryRecord(hashtag)"
>
<span
v-if=
"lastHistoryRecord(hashtag).accounts == 1"
>
{{
$t
(
'
search.person_talking
'
,
{
count
:
lastHistoryRecord
(
hashtag
).
accounts
}
)
}}
<
/span
>
<
span
v
-
else
>
{{
$t
(
'
search.people_talking
'
,
{
count
:
lastHistoryRecord
(
hashtag
).
accounts
}
)
}}
<
/span
>
<
/div
>
<
/div
>
<
div
v
-
if
=
"
lastHistoryRecord(hashtag)
"
class
=
"
count
"
>
{{
lastHistoryRecord
(
hashtag
).
uses
}}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"
search-result-footer text-center panel-footer faint
"
/>
<
/div
>
<
/template
>
<
script
src
=
"
./search.js
"
><
/script
>
<
style
lang
=
"
scss
"
>
@
import
'
../../_variables.scss
'
;
.
search
-
result
-
heading
{
color
:
$fallback
--
faint
;
color
:
var
(
--
faint
,
$fallback
--
faint
);
padding
:
0.75
rem
;
text
-
align
:
center
;
}
@
media
all
and
(
max
-
width
:
800
px
)
{
.
search
-
nav
-
heading
{
.
tab
-
switcher
.
tabs
.
tab
-
wrapper
{
display
:
block
;
justify
-
content
:
center
;
flex
:
1
1
auto
;
text
-
align
:
center
;
}
}
}
.
search
-
result
{
box
-
sizing
:
border
-
box
;
border
-
bottom
:
1
px
solid
;
border
-
color
:
$fallback
--
border
;
border
-
color
:
var
(
--
border
,
$fallback
--
border
);
}
.
search
-
result
-
footer
{
border
-
width
:
1
px
0
0
0
;
border
-
style
:
solid
;
border
-
color
:
var
(
--
border
,
$fallback
--
border
);
padding
:
10
px
;
background
-
color
:
$fallback
--
fg
;
background
-
color
:
var
(
--
panel
,
$fallback
--
fg
);
}
.
search
-
input
-
container
{
padding
:
0.8
rem
;
display
:
flex
;
justify
-
content
:
center
;
.
search
-
input
{
width
:
100
%
;
line
-
height
:
1.125
rem
;
font
-
size
:
1
rem
;
padding
:
0.5
rem
;
box
-
sizing
:
border
-
box
;
}
.
search
-
button
{
margin
-
left
:
0.5
em
;
}
}
.
loading
-
icon
{
padding
:
1
em
;
}
.
trend
{
display
:
flex
;
align
-
items
:
center
;
.
hashtag
{
flex
:
1
1
auto
;
color
:
$fallback
--
text
;
color
:
var
(
--
text
,
$fallback
--
text
);
overflow
:
hidden
;
text
-
overflow
:
ellipsis
;
white
-
space
:
nowrap
;
}
.
count
{
flex
:
0
0
auto
;
width
:
2
rem
;
font
-
size
:
1.5
rem
;
line
-
height
:
2.25
rem
;
font
-
weight
:
500
;
text
-
align
:
center
;
color
:
$fallback
--
text
;
color
:
var
(
--
text
,
$fallback
--
text
);
}
}
<
/style
>
src/components/
user_finder/user_finde
r.js
→
src/components/
search_bar/search_ba
r.js
View file @
e9b45257
const
UserFinde
r
=
{
const
SearchBa
r
=
{
data
:
()
=>
({
username
:
undefined
,
searchTerm
:
undefined
,
hidden
:
true
,
error
:
false
,
loading
:
false
}),
watch
:
{
'
$route
'
:
function
(
route
)
{
if
(
route
.
name
===
'
search
'
)
{
this
.
searchTerm
=
route
.
query
.
query
}
}
},
methods
:
{
find
User
(
username
)
{
this
.
$router
.
push
({
name
:
'
user-search
'
,
query
:
{
query
:
username
}
})
this
.
$refs
.
userS
earchInput
.
focus
()
find
(
searchTerm
)
{
this
.
$router
.
push
({
name
:
'
search
'
,
query
:
{
query
:
searchTerm
}
})
this
.
$refs
.
s
earchInput
.
focus
()
},
toggleHidden
()
{
this
.
hidden
=
!
this
.
hidden
...
...
@@ -17,4 +24,4 @@ const UserFinder = {
}
}
export
default
UserFinde
r
export
default
SearchBa
r
src/components/
user_finder/user_finde
r.vue
→
src/components/
search_bar/search_ba
r.vue
View file @
e9b45257
<
template
>
<div>
<div
class=
"
user-finde
r-container"
>
<div
class=
"
search-ba
r-container"
>
<i
v-if=
"loading"
class=
"icon-spin4
user-
finder-icon animate-spin-slow"
class=
"icon-spin4 finder-icon animate-spin-slow"
/>
<a
v-if=
"hidden"
href=
"#"
:title=
"$t('
finder.find_user
')"
:title=
"$t('
nav.search
')"
><i
class=
"
icon-user-plus user-finder-icon
"
class=
"
button-icon icon-search
"
@
click.prevent.stop=
"toggleHidden"
/></a>
<template
v-else
>
<input
id=
"
user-finde
r-input"
ref=
"
userS
earchInput"
v-model=
"
username
"
class=
"
user-finde
r-input"
:placeholder=
"$t('
finder.find_user
')"
id=
"
search-ba
r-input"
ref=
"
s
earchInput"
v-model=
"
searchTerm
"
class=
"
search-ba
r-input"
:placeholder=
"$t('
nav.search
')"
type=
"text"
@
keyup.enter=
"find
User(username
)"
@
keyup.enter=
"find
(searchTerm
)"
>
<button
class=
"btn search-button"
@
click=
"find
User(username
)"
@
click=
"find
(searchTerm
)"
>
<i
class=
"icon-search"
/>
</button>
<i
class=
"button-icon icon-cancel
user-finder-icon
"
class=
"button-icon icon-cancel"
@
click.prevent.stop=
"toggleHidden"
/>
</
template
>
...
...
@@ -38,22 +38,24 @@
</div>
</template>
<
script
src=
"./
user_finde
r.js"
></
script
>
<
script
src=
"./
search_ba
r.js"
></
script
>
<
style
lang=
"scss"
>
@import
'../../_variables.scss'
;
.
user-finde
r-container
{
.
search-ba
r-container
{
max-width
:
100%
;
display
:
inline-flex
;
align-items
:
baseline
;
vertical-align
:
baseline
;
justify-content
:
flex-end
;
.
user-finde
r-input
,
.
search-ba
r-input
,
.search-button
{
height
:
29px
;
}
.user-finder-input
{
.search-bar-input
{
// TODO: do this properly without a rough guesstimate of 2 icons + paddings
max-width
:
calc
(
100%
-
30px
-
30px
-
20px
);
}
...
...
@@ -62,6 +64,10 @@
margin-left
:
.5em
;
margin-right
:
.5em
;
}
.icon-cancel
{
cursor
:
pointer
;
}
}
</
style
>
src/components/side_drawer/side_drawer.vue
View file @
e9b45257
...
...
@@ -100,8 +100,8 @@
</ul>
<ul>
<li
@
click=
"toggleDrawer"
>
<router-link
:to=
"
{ name: '
user-
search' }">
{{
$t
(
"
nav.
user_
search
"
)
}}
<router-link
:to=
"
{ name: 'search' }">
{{
$t
(
"
nav.search
"
)
}}
</router-link>
</li>
<li
...
...
src/components/tab_switcher/tab_switcher.js
View file @
e9b45257
...
...
@@ -4,7 +4,7 @@ import './tab_switcher.scss'
export
default
Vue
.
component
(
'
tab-switcher
'
,
{
name
:
'
TabSwitcher
'
,
props
:
[
'
renderOnlyFocused
'
,
'
onSwitch
'
],
props
:
[
'
renderOnlyFocused
'
,
'
onSwitch
'
,
'
customActive
'
],
data
()
{
return
{
active
:
this
.
$slots
.
default
.
findIndex
(
_
=>
_
.
tag
)
...
...
@@ -24,6 +24,14 @@ export default Vue.component('tab-switcher', {
}
this
.
active
=
index
}
},
isActiveTab
(
index
)
{
const
customActiveIndex
=
this
.
$slots
.
default
.
findIndex
(
slot
=>
{
const
dataFilter
=
slot
.
data
&&
slot
.
data
.
attrs
&&
slot
.
data
.
attrs
[
'
data-filter
'
]
return
this
.
customActive
&&
this
.
customActive
===
dataFilter
})
return
customActiveIndex
>
-
1
?
customActiveIndex
===
index
:
index
===
this
.
active
}
},
render
(
h
)
{
...
...
@@ -33,7 +41,7 @@ export default Vue.component('tab-switcher', {
const
classesTab
=
[
'
tab
'
]
const
classesWrapper
=
[
'
tab-wrapper
'
]
if
(
index
===
this
.
active
)
{
if
(
this
.
isActiveTab
(
index
)
)
{
classesTab
.
push
(
'
active
'
)
classesWrapper
.
push
(
'
active
'
)
}
...
...
src/components/user_search/user_search.js
deleted
100644 → 0
View file @
0c064105
import
FollowCard
from
'
../follow_card/follow_card.vue
'
import
map
from
'
lodash/map
'
const
userSearch
=
{
components
:
{
FollowCard
},
props
:
[
'
query
'
],
data
()
{
return
{
username
:
''
,
userIds
:
[],
loading
:
false
}
},
computed
:
{
users
()
{
return
this
.
userIds
.
map
(
userId
=>
this
.
$store
.
getters
.
findUser
(
userId
))
}
},
mounted
()
{
this
.
search
(
this
.
query
)
},
watch
:
{
query
(
newV
)
{
this
.
search
(
newV
)
}
},
methods
:
{
newQuery
(
query
)
{
this
.
$router
.
push
({
name
:
'
user-search
'
,
query
:
{
query
}
})
this
.
$refs
.
userSearchInput
.
focus
()
},
search
(
query
)
{
if
(
!
query
)
{
return
}
this
.
loading
=
true
this
.
userIds
=
[]
this
.
$store
.
dispatch
(
'
searchUsers
'
,
query
)
.
then
((
res
)
=>
{
this
.
userIds
=
map
(
res
,
'
id
'
)
})
.
finally
(()
=>
{
this
.
loading
=
false
})
}
}
}
export
default
userSearch
src/components/user_search/user_search.vue
deleted
100644 → 0
View file @
0c064105
<
template
>
<div
class=
"user-search panel panel-default"
>
<div
class=
"panel-heading"
>
{{
$t
(
'
nav.user_search
'
)
}}
</div>
<div
class=
"user-search-input-container"
>
<input
ref=
"userSearchInput"
v-model=
"username"
class=
"user-finder-input"
:placeholder=
"$t('finder.find_user')"
@
keyup.enter=
"newQuery(username)"
>
<button
class=
"btn search-button"
@
click=
"newQuery(username)"
>
<i
class=
"icon-search"
/>
</button>
</div>
<div
v-if=
"loading"
class=
"text-center loading-icon"
>