user_card.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <template>
  2. <div class="user-card" :class="classes" :style="style">
  3. <div class="panel-heading">
  4. <div class='user-info'>
  5. <div class='container'>
  6. <router-link :to="userProfileLink(user)">
  7. <UserAvatar :betterShadow="betterShadow" :src="user.profile_image_url_original"/>
  8. </router-link>
  9. <div class="name-and-screen-name">
  10. <div class="top-line">
  11. <div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div>
  12. <div :title="user.name" class='user-name' v-else>{{user.name}}</div>
  13. <router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
  14. <i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i>
  15. </router-link>
  16. <a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
  17. <i class="icon-link-ext usersettings"></i>
  18. </a>
  19. </div>
  20. <router-link class='user-screen-name' :to="userProfileLink(user)">
  21. <span class="handle">@{{user.screen_name}}
  22. <span class="alert staff" v-if="!hideBio && !!visibleRole">{{visibleRole}}</span>
  23. </span><span v-if="user.locked"><i class="icon icon-lock"></i></span>
  24. <span v-if="!hideUserStatsLocal && !hideBio" class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span>
  25. </router-link>
  26. </div>
  27. </div>
  28. <div class="user-meta">
  29. <div v-if="user.follows_you && loggedIn && isOtherUser" class="following">
  30. {{ $t('user_card.follows_you') }}
  31. </div>
  32. <div class="highlighter" v-if="isOtherUser && (loggedIn || !switcher)">
  33. <!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
  34. <input class="userHighlightText" type="text" :id="'userHighlightColorTx'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/>
  35. <input class="userHighlightCl" type="color" :id="'userHighlightColor'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/>
  36. <label for="style-switcher" class='userHighlightSel select'>
  37. <select class="userHighlightSel" :id="'userHighlightSel'+user.id" v-model="userHighlightType">
  38. <option value="disabled">No highlight</option>
  39. <option value="solid">Solid bg</option>
  40. <option value="striped">Striped bg</option>
  41. <option value="side">Side stripe</option>
  42. </select>
  43. <i class="icon-down-open"/>
  44. </label>
  45. </div>
  46. </div>
  47. <div v-if="isOtherUser" class="user-interactions">
  48. <div class="follow" v-if="loggedIn">
  49. <span v-if="user.following">
  50. <!--Following them!-->
  51. <button @click="unfollowUser" class="pressed" :disabled="followRequestInProgress" :title="$t('user_card.follow_unfollow')">
  52. <template v-if="followRequestInProgress">
  53. {{ $t('user_card.follow_progress') }}
  54. </template>
  55. <template v-else>
  56. {{ $t('user_card.following') }}
  57. </template>
  58. </button>
  59. </span>
  60. <span v-if="!user.following">
  61. <button @click="followUser" :disabled="followRequestInProgress" :title="followRequestSent ? $t('user_card.follow_again') : ''">
  62. <template v-if="followRequestInProgress">
  63. {{ $t('user_card.follow_progress') }}
  64. </template>
  65. <template v-else-if="followRequestSent">
  66. {{ $t('user_card.follow_sent') }}
  67. </template>
  68. <template v-else>
  69. {{ $t('user_card.follow') }}
  70. </template>
  71. </button>
  72. </span>
  73. </div>
  74. <div class='mute' v-if='isOtherUser && loggedIn'>
  75. <span v-if='user.muted'>
  76. <button @click="toggleMute" class="pressed">
  77. {{ $t('user_card.muted') }}
  78. </button>
  79. </span>
  80. <span v-if='!user.muted'>
  81. <button @click="toggleMute">
  82. {{ $t('user_card.mute') }}
  83. </button>
  84. </span>
  85. </div>
  86. <div class="remote-follow" v-if='!loggedIn && user.is_local'>
  87. <form method="POST" :action='subscribeUrl'>
  88. <input type="hidden" name="nickname" :value="user.screen_name">
  89. <input type="hidden" name="profile" value="">
  90. <button click="submit" class="remote-button">
  91. {{ $t('user_card.remote_follow') }}
  92. </button>
  93. </form>
  94. </div>
  95. <div class='block' v-if='isOtherUser && loggedIn'>
  96. <span v-if='user.statusnet_blocking'>
  97. <button @click="unblockUser" class="pressed">
  98. {{ $t('user_card.blocked') }}
  99. </button>
  100. </span>
  101. <span v-if='!user.statusnet_blocking'>
  102. <button @click="blockUser">
  103. {{ $t('user_card.block') }}
  104. </button>
  105. </span>
  106. </div>
  107. </div>
  108. </div>
  109. </div>
  110. <div class="panel-body" v-if="!hideBio">
  111. <div v-if="!hideUserStatsLocal && switcher" class="user-counts">
  112. <div class="user-count" v-on:click.prevent="setProfileView('statuses')">
  113. <h5>{{ $t('user_card.statuses') }}</h5>
  114. <span>{{user.statuses_count}} <br></span>
  115. </div>
  116. <div class="user-count" v-on:click.prevent="setProfileView('friends')">
  117. <h5>{{ $t('user_card.followees') }}</h5>
  118. <span>{{user.friends_count}}</span>
  119. </div>
  120. <div class="user-count" v-on:click.prevent="setProfileView('followers')">
  121. <h5>{{ $t('user_card.followers') }}</h5>
  122. <span>{{user.followers_count}}</span>
  123. </div>
  124. </div>
  125. <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="user-card-bio" v-html="user.description_html"></p>
  126. <p v-else-if="!hideBio" class="user-card-bio">{{ user.description }}</p>
  127. </div>
  128. </div>
  129. </template>
  130. <script src="./user_card.js"></script>
  131. <style lang="scss">
  132. @import '../../_variables.scss';
  133. .user-card {
  134. background-size: cover;
  135. overflow: hidden;
  136. .panel-heading {
  137. padding: .5em 0;
  138. text-align: center;
  139. box-shadow: none;
  140. background: transparent;
  141. flex-direction: column;
  142. align-items: stretch;
  143. }
  144. .panel-body {
  145. word-wrap: break-word;
  146. background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
  147. background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%);
  148. }
  149. p {
  150. margin-bottom: 0;
  151. }
  152. &-bio {
  153. text-align: center;
  154. }
  155. // Modifiers
  156. &-rounded-t {
  157. border-top-left-radius: $fallback--panelRadius;
  158. border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
  159. border-top-right-radius: $fallback--panelRadius;
  160. border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
  161. }
  162. &-rounded {
  163. border-radius: $fallback--panelRadius;
  164. border-radius: var(--panelRadius, $fallback--panelRadius);
  165. }
  166. &-bordered {
  167. border-width: 1px;
  168. border-style: solid;
  169. border-color: $fallback--border;
  170. border-color: var(--border, $fallback--border);
  171. }
  172. }
  173. .user-info {
  174. color: $fallback--lightText;
  175. color: var(--lightText, $fallback--lightText);
  176. padding: 0 26px;
  177. .container {
  178. padding: 16px 0 6px;
  179. display: flex;
  180. max-height: 56px;
  181. .avatar {
  182. flex: 1 0 100%;
  183. width: 56px;
  184. height: 56px;
  185. box-shadow: 0px 1px 8px rgba(0,0,0,0.75);
  186. box-shadow: var(--avatarShadow);
  187. object-fit: cover;
  188. }
  189. }
  190. &:hover .animated.avatar {
  191. canvas {
  192. display: none;
  193. }
  194. img {
  195. visibility: visible;
  196. }
  197. }
  198. .usersettings {
  199. color: $fallback--lightText;
  200. color: var(--lightText, $fallback--lightText);
  201. opacity: .8;
  202. }
  203. .name-and-screen-name {
  204. display: block;
  205. margin-left: 0.6em;
  206. text-align: left;
  207. text-overflow: ellipsis;
  208. white-space: nowrap;
  209. flex: 1 1 0;
  210. // This is so that text doesn't get overlapped by avatar's shadow if it has
  211. // big one
  212. z-index: 1;
  213. img {
  214. width: 26px;
  215. height: 26px;
  216. vertical-align: middle;
  217. object-fit: contain
  218. }
  219. .top-line {
  220. display: flex;
  221. }
  222. }
  223. .user-name {
  224. text-overflow: ellipsis;
  225. overflow: hidden;
  226. flex: 1 1 auto;
  227. margin-right: 1em;
  228. font-size: 15px;
  229. img {
  230. object-fit: contain;
  231. height: 16px;
  232. width: 16px;
  233. vertical-align: middle;
  234. }
  235. }
  236. .user-screen-name {
  237. color: $fallback--lightText;
  238. color: var(--lightText, $fallback--lightText);
  239. display: inline-block;
  240. font-weight: light;
  241. font-size: 15px;
  242. padding-right: 0.1em;
  243. width: 100%;
  244. display: flex;
  245. .dailyAvg {
  246. min-width: 1px;
  247. flex: 0 0 auto;
  248. margin-left: 1em;
  249. font-size: 0.7em;
  250. color: $fallback--text;
  251. color: var(--text, $fallback--text);
  252. }
  253. .handle {
  254. min-width: 1px;
  255. flex: 0 1 auto;
  256. text-overflow: ellipsis;
  257. overflow: hidden;
  258. }
  259. // TODO use proper colors
  260. .staff {
  261. text-transform: capitalize;
  262. color: $fallback--text;
  263. color: var(--btnText, $fallback--text);
  264. background-color: $fallback--fg;
  265. background-color: var(--btn, $fallback--fg);
  266. }
  267. }
  268. .user-meta {
  269. margin-bottom: .15em;
  270. display: flex;
  271. align-items: baseline;
  272. font-size: 14px;
  273. line-height: 22px;
  274. flex-wrap: wrap;
  275. .following {
  276. flex: 1 0 auto;
  277. margin: 0;
  278. margin-bottom: .25em;
  279. text-align: left;
  280. }
  281. .highlighter {
  282. flex: 0 1 auto;
  283. display: flex;
  284. flex-wrap: wrap;
  285. margin-right: -.5em;
  286. align-self: start;
  287. .userHighlightCl {
  288. padding: 2px 10px;
  289. flex: 1 0 auto;
  290. }
  291. .userHighlightSel,
  292. .userHighlightSel.select {
  293. padding-top: 0;
  294. padding-bottom: 0;
  295. flex: 1 0 auto;
  296. }
  297. .userHighlightSel.select i {
  298. line-height: 22px;
  299. }
  300. .userHighlightText {
  301. width: 70px;
  302. flex: 1 0 auto;
  303. }
  304. .userHighlightCl,
  305. .userHighlightText,
  306. .userHighlightSel,
  307. .userHighlightSel.select {
  308. height: 22px;
  309. vertical-align: top;
  310. margin-right: .5em;
  311. margin-bottom: .25em;
  312. }
  313. }
  314. }
  315. .user-interactions {
  316. display: flex;
  317. flex-flow: row wrap;
  318. justify-content: space-between;
  319. margin-right: -.75em;
  320. div {
  321. flex: 1 0 0;
  322. margin-right: .75em;
  323. margin-bottom: .6em;
  324. white-space: nowrap;
  325. }
  326. .mute {
  327. max-width: 220px;
  328. min-height: 28px;
  329. }
  330. .remote-follow {
  331. max-width: 220px;
  332. min-height: 28px;
  333. }
  334. .follow {
  335. max-width: 220px;
  336. min-height: 28px;
  337. }
  338. button {
  339. width: 100%;
  340. height: 100%;
  341. margin: 0;
  342. }
  343. .remote-button {
  344. height: 28px !important;
  345. width: 92%;
  346. }
  347. .pressed {
  348. border-bottom-color: rgba(255, 255, 255, 0.2);
  349. border-top-color: rgba(0, 0, 0, 0.2);
  350. }
  351. }
  352. }
  353. .user-counts {
  354. display: flex;
  355. line-height:16px;
  356. padding: .5em 1.5em 0em 1.5em;
  357. text-align: center;
  358. justify-content: space-between;
  359. color: $fallback--lightText;
  360. color: var(--lightText, $fallback--lightText);
  361. flex-wrap: wrap;
  362. }
  363. .user-count {
  364. flex: 1 0 auto;
  365. padding: .5em 0 .5em 0;
  366. margin: 0 .5em;
  367. h5 {
  368. font-size:1em;
  369. font-weight: bolder;
  370. margin: 0 0 0.25em;
  371. }
  372. a {
  373. text-decoration: none;
  374. }
  375. }
  376. </style>