Index.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <template>
  2. <div class="landing-index-component">
  3. <section class="page-wrapper">
  4. <div class="container container-compact">
  5. <div class="card bg-bluegray-900" style="border-radius: 10px;">
  6. <div class="card-header bg-bluegray-800 nav-menu" style="border-top-left-radius: 10px; border-top-right-radius: 10px;">
  7. <ul class="nav justify-content-around">
  8. <li class="nav-item">
  9. <router-link to="/" class="nav-link">About</router-link>
  10. </li>
  11. <li v-if="config.show_directory" class="nav-item">
  12. <router-link to="/web/directory" class="nav-link">Directory</router-link>
  13. </li>
  14. <li v-if="config.show_explore_feed" class="nav-item">
  15. <router-link to="/web/explore" class="nav-link">Explore</router-link>
  16. </li>
  17. </ul>
  18. </div>
  19. <div class="card-img-top p-2">
  20. <img
  21. :src="config.about.banner_image"
  22. class="img-fluid rounded"
  23. style="width: 100%;max-height: 200px;object-fit: cover;"
  24. alt="Server banner image"
  25. height="200"
  26. onerror="this.src='/storage/headers/default.jpg';this.onerror=null;">
  27. </div>
  28. <div class="card-body">
  29. <div class="server-header">
  30. <p class="server-header-domain">{{ config.domain }}</p>
  31. <p class="server-header-attribution">
  32. Decentralized photo sharing social media powered by <a href="https://pixelfed.org" target="_blank">Pixelfed</a>
  33. </p>
  34. </div>
  35. <div class="server-stats">
  36. <div class="list-group">
  37. <div class="list-group-item bg-transparent">
  38. <p class="stat-value">{{ formatCount(config.stats.posts_count) }}</p>
  39. <p class="stat-label">Posts</p>
  40. </div>
  41. <div class="list-group-item bg-transparent">
  42. <p class="stat-value">{{ formatCount(config.stats.active_users) }}</p>
  43. <p class="stat-label">Active Users</p>
  44. </div>
  45. <div class="list-group-item bg-transparent">
  46. <p class="stat-value">{{ formatCount(config.stats.total_users) }}</p>
  47. <p class="stat-label">Total Users</p>
  48. </div>
  49. </div>
  50. </div>
  51. <div class="server-admin">
  52. <div class="list-group">
  53. <div v-if="config.contact.account" class="list-group-item bg-transparent">
  54. <p class="item-label">Managed By</p>
  55. <a :href="config.contact.account.url" class="admin-card" target="_blank">
  56. <div class="d-flex">
  57. <img
  58. :src="config.contact.account.avatar"
  59. width="45"
  60. height="45"
  61. class="avatar"
  62. :alt="`${config.contact.account.username}'s avatar`"
  63. onerror="this.src='/storage/avatars/default.jpg';this.onerror=null;"
  64. >
  65. <div class="user-info">
  66. <p class="display-name">{{ config.contact.account.display_name }}</p>
  67. <p class="username">&commat;{{ config.contact.account.username }}</p>
  68. </div>
  69. </div>
  70. </a>
  71. </div>
  72. <div v-if="config.contact.email" class="list-group-item bg-transparent">
  73. <p class="item-label">Contact</p>
  74. <a :href="`mailto:${config.contact.email}?subject=Regarding ${config.domain}`" class="admin-email" target="_blank">{{ config.contact.email }}</a>
  75. </div>
  76. </div>
  77. </div>
  78. <div class="accordion" id="accordion">
  79. <div class="card bg-bluegray-700">
  80. <div class="card-header bg-bluegray-800" id="headingOne">
  81. <h2 class="mb-0">
  82. <button class="btn btn-link btn-block" type="button" data-toggle="collapse" data-target="#collapseOne" aria-controls="collapseOne" @click="toggleAccordion(0)">
  83. <span class="text-white h5">
  84. <i class="far fa-info-circle mr-2 text-muted"></i>
  85. About
  86. </span>
  87. <i class="far" :class="[ accordionTab === 0 ? 'fa-chevron-left text-primary': 'fa-chevron-down']"></i>
  88. </button>
  89. </h2>
  90. </div>
  91. <div id="collapseOne" class="collapse" aria-labelledby="headingOne" data-parent="#accordion">
  92. <div class="card-body about-text">
  93. <p v-html="config.about.description"></p>
  94. </div>
  95. </div>
  96. </div>
  97. <div class="card bg-bluegray-700">
  98. <div class="card-header bg-bluegray-800" id="headingTwo">
  99. <h2 class="mb-0">
  100. <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo" @click="toggleAccordion(1)">
  101. <span class="text-white h5">
  102. <i class="far fa-list mr-2 text-muted"></i>
  103. Server Rules
  104. </span>
  105. <i class="far" :class="[ accordionTab === 1 ? 'fa-chevron-left text-primary': 'fa-chevron-down']"></i>
  106. </button>
  107. </h2>
  108. </div>
  109. <div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
  110. <div class="card-body">
  111. <div class="list-group list-group-rules">
  112. <div v-for="rule in config.rules" class="list-group-item bg-bluegray-900">
  113. <div class="rule-id">{{ rule.id }}</div>
  114. <div class="rule-text">{{ rule.text }}</div>
  115. </div>
  116. </div>
  117. </div>
  118. </div>
  119. </div>
  120. <div class="card bg-bluegray-700">
  121. <div class="card-header bg-bluegray-800" id="headingThree">
  122. <h2 class="mb-0">
  123. <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree" @click="toggleAccordion(2)">
  124. <span class="text-white h5">
  125. <i class="far fa-sparkles mr-2 text-muted"></i>
  126. Supported Features
  127. </span>
  128. <i class="far" :class="[ accordionTab === 2 ? 'fa-chevron-left text-primary': 'fa-chevron-down']"></i>
  129. </button>
  130. </h2>
  131. </div>
  132. <div id="collapseThree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion">
  133. <div class="card-body card-features">
  134. <div class="card-features-cloud">
  135. <div class="badge badge-success"><i class="far fa-check-circle"></i> Photo Posts</div>
  136. <div class="badge badge-success"><i class="far fa-check-circle"></i> Photo Albums</div>
  137. <div class="badge badge-success"><i class="far fa-check-circle"></i> Photo Filters</div>
  138. <div class="badge badge-success"><i class="far fa-check-circle"></i> Collections</div>
  139. <div class="badge badge-success"><i class="far fa-check-circle"></i> Comments</div>
  140. <div class="badge badge-success"><i class="far fa-check-circle"></i> Hashtags</div>
  141. <div class="badge badge-success"><i class="far fa-check-circle"></i> Likes</div>
  142. <div class="badge badge-success"><i class="far fa-check-circle"></i> Notifications</div>
  143. <div class="badge badge-success"><i class="far fa-check-circle"></i> Shares</div>
  144. </div>
  145. <div class="py-3">
  146. <p class="lead">
  147. <span>You can share up to <span class="font-weight-bold">{{ config.uploader.album_limit }}</span> photos*</span>
  148. <span v-if="config.features.video">or <span class="font-weight-bold">1</span> video*</span>
  149. <span>at a time with a max caption length of <span class="font-weight-bold">{{ config.uploader.max_caption_length }}</span> characters.</span>
  150. </p>
  151. <p class="small opacity-50">* - Maximum file size is {{ formatBytes(config.uploader.max_photo_size) }}</p>
  152. </div>
  153. <div class="list-group list-group-features">
  154. <div class="list-group-item bg-bluegray-900">
  155. <div class="feature-label">Federation</div>
  156. <i class="far fa-lg" :class="[config.features.federation ? 'fa-check-circle' : 'fa-times-circle' ]"></i>
  157. </div>
  158. <div class="list-group-item bg-bluegray-900">
  159. <div class="feature-label">Mobile App Support</div>
  160. <i class="far fa-lg" :class="[config.features.mobile_apis ? 'fa-check-circle' : 'fa-times-circle' ]"></i>
  161. </div>
  162. <div class="list-group-item bg-bluegray-900">
  163. <div class="feature-label">Stories</div>
  164. <i class="far fa-lg" :class="[config.features.stories ? 'fa-check-circle' : 'fa-times-circle' ]"></i>
  165. </div>
  166. <div class="list-group-item bg-bluegray-900">
  167. <div class="feature-label">Videos</div>
  168. <i class="far fa-lg" :class="[config.features.video ? 'fa-check-circle' : 'fa-times-circle' ]"></i>
  169. </div>
  170. </div>
  171. </div>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. </div>
  177. </div>
  178. <footer-component />
  179. </section>
  180. </div>
  181. </template>
  182. <script type="text/javascript">
  183. export default {
  184. data() {
  185. return {
  186. config: window.pfl,
  187. accordionTab: undefined
  188. }
  189. },
  190. methods: {
  191. toggleAccordion(idx) {
  192. if(this.accordionTab == idx) {
  193. this.accordionTab = undefined;
  194. return;
  195. }
  196. this.accordionTab = idx;
  197. },
  198. formatCount(val) {
  199. if(!val) {
  200. return 0;
  201. }
  202. return val.toLocaleString('en-CA', { compactDisplay: "short", notation: "compact"});
  203. },
  204. formatBytes(bytes, unit = 'megabyte') {
  205. const units = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte'];
  206. const navigatorLocal = navigator.languages && navigator.languages.length >= 0 ? navigator.languages[0] : 'en-US';
  207. const unitIndex = Math.max(0, Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1));
  208. return Intl.NumberFormat(navigatorLocal, {
  209. style: 'unit',
  210. unit : units[unitIndex],
  211. useGrouping: false,
  212. maximumFractionDigits: 0,
  213. roundingMode: 'ceil'
  214. }).format(bytes / (1024 ** unitIndex))
  215. }
  216. }
  217. }
  218. </script>