Compare commits

...

11 Commits

Author SHA1 Message Date
ChunkyProgrammer c8a0ef0edd
Merge c6e05a5b97 into 43a7fbdcb1 2024-04-27 03:07:32 +00:00
NEXI 43a7fbdcb1
Translated using Weblate (Serbian)
Currently translated at 100.0% (830 of 830 strings)

Translation: FreeTube/Translations
Translate-URL: https://hosted.weblate.org/projects/free-tube/translations/sr/
2024-04-27 05:07:17 +02:00
absidue 88bed9eaf6
Fix handling of emojis with ZWJ sequences in profile initials (#5023) 2024-04-27 10:53:03 +08:00
Sergio Marques 7f3925d0c5
Translated using Weblate (Portuguese)
Currently translated at 99.3% (825 of 830 strings)

Translation: FreeTube/Translations
Translate-URL: https://hosted.weblate.org/projects/free-tube/translations/pt/
2024-04-27 02:07:20 +02:00
ChunkyProgrammer c6e05a5b97 Merge branch 'development' into add-command-line-arg-to-search 2024-04-16 17:21:00 -04:00
ChunkyProgrammer 11973f9597 Merge branch 'development' into add-command-line-arg-to-search 2024-04-12 09:12:21 -04:00
ChunkyProgrammer 39e6936b7b Merge branch 'development' into add-command-line-arg-to-search 2024-03-25 10:31:39 -04:00
ChunkyProgrammer 40c1c01b4b Merge branch 'development' into add-command-line-arg-to-search 2024-03-07 20:18:02 -05:00
ChunkyProgrammer d9747bc476 url encode search query 2024-02-13 13:38:57 -05:00
ChunkyProgrammer b46553a24b Merge branch 'development' into add-command-line-arg-to-search 2024-02-13 12:33:10 -05:00
ChunkyProgrammer 5e5e6d2af2 Add command line arg to search FreeTube
https://github.com/FreeTubeApp/FreeTube/issues/1619
2023-11-07 22:53:14 -05:00
12 changed files with 97 additions and 11 deletions

View File

@ -1258,6 +1258,11 @@ function runApp() {
}
function baseUrl(arg) {
// freetube.exe s="query"
if (arg.startsWith('s=')) {
return `https://www.youtube.com/results?search_query=${encodeURIComponent(arg.substring(2))}`
}
let newArg = arg.replace('freetube://', '')
// add support for authority free url
.replace('freetube:', '')

View File

@ -482,7 +482,7 @@ export default defineComponent({
enableSetSearchQueryText: function () {
ipcRenderer.on('updateSearchInputText', (event, searchQueryText) => {
if (searchQueryText) {
this.$refs.topNav.updateSearchInputText(searchQueryText)
this.$refs.topNav.updateSearchInputText({ detail: { query: searchQueryText } })
}
})

View File

@ -1,6 +1,7 @@
import { defineComponent } from 'vue'
import { sanitizeForHtmlId } from '../../helpers/accessibility'
import { MAIN_PROFILE_ID } from '../../../constants'
import { getFirstCharacter } from '../../helpers/strings'
export default defineComponent({
name: 'FtProfileBubble',
@ -24,6 +25,9 @@ export default defineComponent({
},
emits: ['click'],
computed: {
locale: function () {
return this.$i18n.locale.replace('_', '-')
},
isMainProfile: function () {
return this.profileId === MAIN_PROFILE_ID
},
@ -31,7 +35,9 @@ export default defineComponent({
return 'profileBubble' + sanitizeForHtmlId(this.profileId)
},
profileInitial: function () {
return this?.profileName?.length > 0 ? Array.from(this.translatedProfileName)[0].toUpperCase() : ''
return this.profileName
? getFirstCharacter(this.translatedProfileName, this.locale).toUpperCase()
: ''
},
translatedProfileName: function () {
return this.isMainProfile ? this.$t('Profile.All Channels') : this.profileName

View File

@ -8,6 +8,7 @@ import FtButton from '../../components/ft-button/ft-button.vue'
import { MAIN_PROFILE_ID } from '../../../constants'
import { calculateColorLuminance, colors } from '../../helpers/colors'
import { showToast } from '../../helpers/utils'
import { getFirstCharacter } from '../../helpers/strings'
export default defineComponent({
name: 'FtProfileEdit',
@ -47,11 +48,16 @@ export default defineComponent({
}
},
computed: {
locale: function () {
return this.$i18n.locale.replace('_', '-')
},
colorValues: function () {
return colors.map(color => color.value)
},
profileInitial: function () {
return this?.profileName?.length > 0 ? Array.from(this.translatedProfileName)[0].toUpperCase() : ''
return this.profileName
? getFirstCharacter(this.translatedProfileName, this.locale).toUpperCase()
: ''
},
activeProfile: function () {
return this.$store.getters.getActiveProfile

View File

@ -5,6 +5,7 @@ import FtCard from '../../components/ft-card/ft-card.vue'
import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
import { showToast } from '../../helpers/utils'
import { MAIN_PROFILE_ID } from '../../../constants'
import { getFirstCharacter } from '../../helpers/strings'
export default defineComponent({
name: 'FtProfileSelector',
@ -19,6 +20,9 @@ export default defineComponent({
}
},
computed: {
locale: function () {
return this.$i18n.locale.replace('_', '-')
},
profileList: function () {
return this.$store.getters.getProfileList
},
@ -26,12 +30,15 @@ export default defineComponent({
return this.$store.getters.getActiveProfile
},
activeProfileInitial: function () {
// use Array.from, so that emojis don't get split up into individual character codes
return this.activeProfile?.name?.length > 0 ? Array.from(this.translatedProfileName(this.activeProfile))[0].toUpperCase() : ''
return this.activeProfile?.name
? getFirstCharacter(this.translatedProfileName(this.activeProfile), this.locale).toUpperCase()
: ''
},
profileInitials: function () {
return this.profileList.map((profile) => {
return profile?.name?.length > 0 ? Array.from(this.translatedProfileName(profile))[0].toUpperCase() : ''
return profile?.name
? getFirstCharacter(this.translatedProfileName(profile), this.locale).toUpperCase()
: ''
})
}
},

View File

@ -6,6 +6,7 @@ import FtButton from '../../components/ft-button/ft-button.vue'
import { MAIN_PROFILE_ID } from '../../../constants'
import { deepCopy, showToast } from '../../helpers/utils'
import { getFirstCharacter } from '../../helpers/strings'
export default defineComponent({
name: 'FtSubscribeButton',
@ -45,9 +46,14 @@ export default defineComponent({
}
},
computed: {
locale: function () {
return this.$i18n.locale.replace('_', '-')
},
profileInitials: function () {
return this.profileDisplayList.map((profile) => {
return profile?.name?.length > 0 ? Array.from(profile.name)[0].toUpperCase() : ''
return profile.name
? getFirstCharacter(profile.name, this.locale).toUpperCase()
: ''
})
},

View File

@ -0,0 +1,2 @@
const events = new EventTarget()
export default events

View File

@ -3,6 +3,7 @@ import { mapActions } from 'vuex'
import FtInput from '../ft-input/ft-input.vue'
import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue'
import FtProfileSelector from '../ft-profile-selector/ft-profile-selector.vue'
import TopNavEvents from './top-nav-events'
import debounce from 'lodash.debounce'
import { IpcChannels } from '../../../constants'
@ -119,6 +120,11 @@ export default defineComponent({
})
this.debounceSearchResults = debounce(this.getSearchSuggestions, 200)
TopNavEvents.addEventListener('updateSearchInput', this.updateSearchInputText)
},
beforeDestroy: function () {
TopNavEvents.removeEventListener('updateSearchInput', this.updateSearchInputText)
},
methods: {
goToSearch: async function (query, { event }) {
@ -357,8 +363,8 @@ export default defineComponent({
hideFilters: function () {
this.showFilters = false
},
updateSearchInputText: function (text) {
this.$refs.searchInput.updateInputData(text)
updateSearchInputText: function ({ detail: { query } }) {
this.$refs.searchInput.updateInputData(query)
},
...mapActions([
'getYoutubeUrlInfo',

View File

@ -52,3 +52,31 @@ export function translateWindowTitle(title, i18n) {
return null
}
}
/**
* Returns the first user-perceived character,
* respecting language specific rules and
* emojis made up of multiple codepoints
* like flags, families and skin tone modifiers.
* @param {string} text
* @param {string} locale
* @returns {string}
*/
export function getFirstCharacter(text, locale) {
if (text.length === 0) {
return ''
}
// Firefox only received support for Intl.Segmenter support in version 125 (2024-04-16)
// so fallback to Array.from just in case.
// TODO: Remove fallback in the future
if (Intl.Segmenter) {
const segmenter = new Intl.Segmenter([locale, 'en'], { granularity: 'grapheme' })
// Use iterator directly as we only need the first segment
const firstSegment = segmenter.segment(text)[Symbol.iterator]().next().value
return firstSegment.segment
} else {
return Array.from(text)[0]
}
}

View File

@ -11,6 +11,7 @@ import {
} from '../../helpers/utils'
import { getLocalSearchContinuation, getLocalSearchResults } from '../../helpers/api/local'
import { invidiousAPICall } from '../../helpers/api/invidious'
import TopNavEvents from '../../components/top-nav/top-nav-events'
export default defineComponent({
name: 'Search',
@ -54,6 +55,7 @@ export default defineComponent({
// react to route changes...
const query = this.$route.params.query
TopNavEvents.dispatchEvent(new CustomEvent('updateSearchInput', { detail: { query } }))
const searchSettings = {
sortBy: this.$route.query.sortBy,
time: this.$route.query.time,
@ -73,7 +75,10 @@ export default defineComponent({
}
},
mounted: function () {
this.query = this.$route.params.query
const query = this.$route.params.query
this.query = query
TopNavEvents.dispatchEvent(new CustomEvent('updateSearchInput', { detail: { query } }))
this.searchSettings = {
sortBy: this.$route.query.sortBy,
@ -83,7 +88,7 @@ export default defineComponent({
}
const payload = {
query: this.query,
query,
options: {},
searchSettings: this.searchSettings
}

View File

@ -276,6 +276,9 @@ Settings:
Open Link: Abrir ligação
Ask Before Opening Link: Perguntar antes de abrir a ligação
No Action: Nenhuma ação
Auto Load Next Page:
Label: Carregar seguinte automaticamente
Tooltip: Carrega as páginas e comentários automaticamente.
Theme Settings:
Theme Settings: 'Configurações de tema'
Match Top Bar with Main Color: 'Utilizar cor principal na barra superior'
@ -633,6 +636,7 @@ Settings:
Set Password: Definir palavra-passe
Remove Password: Remover palavra-passe
Expand All Settings Sections: Expandir todas as secções de configurações
Sort Settings Sections (A-Z): Ordenar definições (A-Z)
About:
#On About page
About: 'Acerca'
@ -974,6 +978,13 @@ Playlist:
Playlist: Lista de reprodução
Sort By:
Sort By: Ordenar por
AuthorAscending: Autor (A-Z)
DateAddedNewest: Últimas adições primeiro
DateAddedOldest: Primeiras adições primeiro
AuthorDescending: Autor (Z-A)
VideoTitleAscending: Título (A-Z)
VideoTitleDescending: Título (Z-A)
Custom: Personalizado
Toggle Theatre Mode: 'Alternar modo cinema'
Change Format:
Change Media Formats: 'Alterar formatos multimédia'
@ -1192,3 +1203,6 @@ Age Restricted:
Feed:
Feed Last Updated: 'Última atualização de {feedName}: {date}'
Refresh Feed: Recarregar {subscriptionName}
Display Label: '{label}: {value}'
Moments Ago: há momentos
checkmark:

View File

@ -608,6 +608,7 @@ Settings:
Enter Password To Unlock: Унесите лозинку да бисте откључали подешавања
Unlock: Откључај
Expand All Settings Sections: Прошири све одељке подешавања
Sort Settings Sections (A-Z): Сортирање одељка подешавања (A-Z)
About:
#On About page
About: 'О апликацији'