Compare commits
3 Commits
nprofile-q
...
tyiu/fix-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
d946dbe50d
|
|||
|
9590166367
|
|||
|
efdecaf118
|
49
.github/workflows/export-translations.yaml
vendored
Normal file
49
.github/workflows/export-translations.yaml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name: Export Source Translations
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
export-source-translations:
|
||||||
|
name: Update translations branch
|
||||||
|
runs-on: macos-12
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- xcode: "14.2"
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Run export script
|
||||||
|
run: |
|
||||||
|
sh devtools/export-source-translation.sh
|
||||||
|
- name: Push source translations to Transifex
|
||||||
|
uses: transifex/cli-action@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.TX_TOKEN }}
|
||||||
|
args: push --branch ''
|
||||||
|
- name: Remove extraneous /tmp/tx file from running transifex cli that breaks the next pull step
|
||||||
|
run: |
|
||||||
|
rm -rf /tmp/tx
|
||||||
|
- name: Pull translations from Transifex
|
||||||
|
uses: transifex/cli-action@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.TX_TOKEN }}
|
||||||
|
args: pull --branch ''
|
||||||
|
- name: Commit translation changes
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v4
|
||||||
|
with:
|
||||||
|
commit_message: Update Translations 🤖
|
||||||
|
branch: translations
|
||||||
|
create_branch: true
|
||||||
|
push_options: '--force'
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
with:
|
||||||
|
source_branch: "translations"
|
||||||
|
destination_branch: "master"
|
||||||
|
pr_title: "Update Translations 🤖"
|
||||||
|
if: steps.auto-commit-action.outputs.changes_detected == 'true'
|
||||||
|
|
||||||
24
.tx/config
Executable file
24
.tx/config
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
[main]
|
||||||
|
host = https://www.transifex.com
|
||||||
|
lang_map = aa_DJ: aa-DJ, af_ZA: af-ZA, am_ET: am-ET, ar_AA: ar-AA, ar_AE: ar-AE, ar_DZ: ar-DZ, ar_EG: ar-EG, ar_IQ: ar-IQ, ar_JO: ar-JO, ar_LB: ar-LB, ar_SA: ar-SA, ar_SD: ar-SD, ar_SY: ar-SY, as_IN: as-IN, ast_ES: ast-ES, az_AZ: az-AZ, az_IR: az-IR, be_BY: be-BY, bem_ZM: bem-ZM, bg_BG: bg-BG, bg_US: bg-US, bn_BD: bn-BD, bn_IN: bn-IN, bo_CN: bo-CN, bqi_IR: bqi-IR, br_FR: br-FR, bs_BA: bs-BA, bs_BA-SRP: bs-BA-SRP, ca_ES: ca-ES, cs_CZ: cs-CZ, cy_GB: cy-GB, da_DK: da-DK, de_AT: de-AT, de_CH: de-CH, de_DE: de-DE, dz_BT: dz-BT, el_CY: el-CY, el_DE: el-DE, el_GR: el-GR, en_AE: en-AE, en_AL: en-AL, en_AT: en-AT, en_AU: en-AU, en_BA: en-BA, en_BA-SRP: en-BA-SRP, en_BD: en-BD, en_BE: en-BE, en_BG: en-BG, en_BH: en-BH, en_BR: en-BR, en_CA: en-CA, en_CH: en-CH, en_CL: en-CL, en_CO: en-CO, en_CY: en-CY, en_CZ: en-CZ, en_DE: en-DE, en_DK: en-DK, en_EC: en-EC, en_EG: en-EG, en_ES: en-ES, en_FI: en-FI, en_FJ: en-FJ, en_FR: en-FR, en_GB: en-GB, en_GH: en-GH, en_GR: en-GR, en_HK: en-HK, en_HR: en-HR, en_HU: en-HU, en_IE: en-IE, en_IN: en-IN, en_IT: en-IT, en_JP: en-JP, en_KR: en-KR, en_KW: en-KW, en_LK: en-LK, en_MX: en-MX, en_MY: en-MY, en_NG: en-NG, en_NL: en-NL, en_NO: en-NO, en_NZ: en-NZ, en_PE: en-PE, en_PG: en-PG, en_PH: en-PH, en_PK: en-PK, en_PL: en-PL, en_PR: en-PR, en_PT: en-PT, en_QA: en-QA, en_RO: en-RO, en_RS: en-RS, en_SA: en-SA, en_SE: en-SE, en_SG: en-SG, en_SI: en-SI, en_SK: en-SK, en_TT: en-TT, en_UG: en-UG, en_ZA: en-ZA, en_ZM: en-ZM, en_ee: en-ee, en_lt: en-lt, en_lv: en-lv, es_419: es-419, es_AR: es-AR, es_BO: es-BO, es_CL: es-CL, es_CO: es-CO, es_CR: es-CR, es_CU: es-CU, es_DO: es-DO, es_EC: es-EC, es_ES: es-ES, es_GT: es-GT, es_HN: es-HN, es_MX: es-MX, es_NI: es-NI, es_PA: es-PA, es_PE: es-PE, es_PR: es-PR, es_PY: es-PY, es_SA: es-SA, es_SV: es-SV, es_US: es-US, es_UY: es-UY, es_VE: es-VE, et_EE: et-EE, eu_ES: eu-ES, fa_AF: fa-AF, fa_IR: fa-IR, ff_SN: ff-SN, fi_FI: fi-FI, fil_PH: fil-PH, fo_FO: fo-FO, fr_BE: fr-BE, fr_CA: fr-CA, fr_CH: fr-CH, fr_CI: fr-CI, fr_CM: fr-CM, fr_FR: fr-FR, fr_GA: fr-GA, fr_LU: fr-LU, fy_NL: fy-NL, ga_IE: ga-IE, gl_ES: gl-ES, gu_IN: gu-IN, gug_PY: gug-PY, he_IL: he-IL, hi_IN: hi-IN, hr_BA: hr-BA, hr_BA-SRP: hr-BA-SRP, hr_HR: hr-HR, ht_HT: ht-HT, hu_HU: hu-HU, hu_RO: hu-RO, hu_SK: hu-SK, hy_AM: hy-AM, hy_RU: hy-RU, hye_RU: hye-RU, id_ID: id-ID, is_IS: is-IS, it_CH: it-CH, it_IT: it-IT, ja_JP: ja-JP, ka_GE: ka-GE, kk_KZ: kk-KZ, km_KH: km-KH, kn_IN: kn-IN, ko_KR: ko-KR, ks_IN: ks-IN, ku_IQ: ku-IQ, lg_UG: lg-UG, lo_LA: lo-LA, loz_ZM: loz-ZM, lt_LT: lt-LT, lv_LV: lv-LV, mhr_RU: mhr-RU, mk_MK: mk-MK, ml_IN: ml-IN, mn_MN: mn-MN, mr_IN: mr-IN, ms_BN: ms-BN, ms_MY: ms-MY, mt_MT: mt-MT, my_MM: my-MM, nb_NO: nb-NO, ne_NP: ne-NP, nl_BE: nl-BE, nl_NL: nl-NL, nn_NO: nn-NO, no_NO: no-NO, or_IN: or-IN, pa_IN: pa-IN, pa_PK: pa-PK, pl_PL: pl-PL, ps_AF: ps-AF, pt_AO: pt-AO, pt_BR: pt-BR, pt_MZ: pt-MZ, pt_PT: pt-PT, qu_EC: qu-EC, ro_MD: ro-MD, ro_RO: ro-RO, ru_RU: ru-RU, ru_UA: ru-UA, ru_ee: ru-ee, ru_lt: ru-lt, ru_lv: ru-lv, si_LK: si-LK, sk_SK: sk-SK, sl_SI: sl-SI, sq_AL: sq-AL, sr_BA-SRP: sr-BA-SRP, sr_ME: sr-ME, sr_RS: sr-RS, st_ZA: st-ZA, sv_FI: sv-FI, sv_SE: sv-SE, sw_CD: sw-CD, sw_KE: sw-KE, sw_TZ: sw-TZ, sw_UG: sw-UG, ta_IN: ta-IN, ta_LK: ta-LK, te_IN: te-IN, tg_TJ: tg-TJ, th_TH: th-TH, tk_TM: tk-TM, tl_PH: tl-PH, tr_CY: tr-CY, tr_DE: tr-DE, tr_TR: tr-TR, uk_UA: uk-UA, ur_PK: ur-PK, uz_UZ: uz-UZ, vi_VN: vi-VN, wo_SN: wo-SN, yue_CN: yue-CN, zh_CN: zh-CN, zh_HK: zh-HK, zh_SG: zh-SG, zh_TW: zh-TW, zu_ZA: zu-ZA
|
||||||
|
|
||||||
|
[o:damus:p:damus-ios-staging:r:infopliststrings]
|
||||||
|
file_filter = damus/<lang>.lproj/InfoPlist.strings
|
||||||
|
source_file = damus/en-US.xcloc/Source Contents/damus/en-US.lproj/InfoPlist.strings
|
||||||
|
type = STRINGS_UTF8
|
||||||
|
minimum_perc = 0
|
||||||
|
resource_name = damus..en-US.lproj/InfoPlist.strings (translations)
|
||||||
|
|
||||||
|
[o:damus:p:damus-ios-staging:r:localizablestrings]
|
||||||
|
file_filter = damus/<lang>.lproj/Localizable.strings
|
||||||
|
source_file = damus/en-US.xcloc/Source Contents/damus/en-US.lproj/Localizable.strings
|
||||||
|
type = STRINGS_UTF8
|
||||||
|
minimum_perc = 0
|
||||||
|
resource_name = damus..en-US.lproj/Localizable.strings (translations)
|
||||||
|
|
||||||
|
[o:damus:p:damus-ios-staging:r:localizablestringsdict]
|
||||||
|
file_filter = damus/<lang>.lproj/Localizable.stringsdict
|
||||||
|
source_file = damus/en-US.xcloc/Source Contents/damus/en-US.lproj/Localizable.stringsdict
|
||||||
|
type = STRINGSDICT
|
||||||
|
minimum_perc = 0
|
||||||
|
resource_name = damus..en-US.lproj/Localizable.stringsdict (translations)
|
||||||
@@ -20,7 +20,7 @@ struct TranslateView: View {
|
|||||||
@State var translated_artifacts: NoteArtifacts? = nil
|
@State var translated_artifacts: NoteArtifacts? = nil
|
||||||
|
|
||||||
var TranslateButton: some View {
|
var TranslateButton: some View {
|
||||||
Button(NSLocalizedString("Translate Note", comment: "Button to translate note from different language.")) {
|
Button(NSLocalizedString("Translate Note into your language", comment: "Button to translate note from different language.")) {
|
||||||
show_translated_note = true
|
show_translated_note = true
|
||||||
}
|
}
|
||||||
.translate_button_style()
|
.translate_button_style()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ struct ShareAction: View {
|
|||||||
|
|
||||||
HStack(alignment: .top, spacing: 25) {
|
HStack(alignment: .top, spacing: 25) {
|
||||||
|
|
||||||
ShareActionButton(img: "link", text: NSLocalizedString("Copy Link", comment: "Button to copy link to note"), col: col) {
|
ShareActionButton(img: "link", text: NSLocalizedString("Copy Link TempChange", comment: "Button to copy link to note"), col: col) {
|
||||||
show_share_action = false
|
show_share_action = false
|
||||||
UIPasteboard.general.string = "https://damus.io/" + (bech32_note_id(event.id) ?? event.id)
|
UIPasteboard.general.string = "https://damus.io/" + (bech32_note_id(event.id) ?? event.id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Tex
|
|||||||
|
|
||||||
struct PostView: View {
|
struct PostView: View {
|
||||||
@State var post: NSMutableAttributedString = NSMutableAttributedString()
|
@State var post: NSMutableAttributedString = NSMutableAttributedString()
|
||||||
|
@State var cursor: Int = 0
|
||||||
@FocusState var focus: Bool
|
@FocusState var focus: Bool
|
||||||
@State var showPrivateKeyWarning: Bool = false
|
@State var showPrivateKeyWarning: Bool = false
|
||||||
@State var attach_media: Bool = false
|
@State var attach_media: Bool = false
|
||||||
@@ -104,7 +105,7 @@ struct PostView: View {
|
|||||||
|
|
||||||
var TextEntry: some View {
|
var TextEntry: some View {
|
||||||
ZStack(alignment: .topLeading) {
|
ZStack(alignment: .topLeading) {
|
||||||
TextViewWrapper(attributedText: $post)
|
TextViewWrapper(attributedText: $post, cursor: $cursor)
|
||||||
.focused($focus)
|
.focused($focus)
|
||||||
.textInputAutocapitalization(.sentences)
|
.textInputAutocapitalization(.sentences)
|
||||||
.onChange(of: post) { _ in
|
.onChange(of: post) { _ in
|
||||||
@@ -191,7 +192,7 @@ struct PostView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
let searching = get_searching_string(post.string)
|
let searching = get_searching_string(post.string, cursor: cursor)
|
||||||
|
|
||||||
TopBar
|
TopBar
|
||||||
|
|
||||||
@@ -204,7 +205,7 @@ struct PostView: View {
|
|||||||
|
|
||||||
// This if-block observes @ for tagging
|
// This if-block observes @ for tagging
|
||||||
if let searching {
|
if let searching {
|
||||||
UserSearch(damus_state: damus_state, search: searching, post: $post)
|
UserSearch(damus_state: damus_state, search: searching, post: $post, cursor: $cursor)
|
||||||
.frame(maxHeight: .infinity)
|
.frame(maxHeight: .infinity)
|
||||||
} else {
|
} else {
|
||||||
Divider()
|
Divider()
|
||||||
@@ -253,8 +254,12 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_searching_string(_ post: String) -> String? {
|
func get_searching_string(_ post: String, cursor: Int) -> String? {
|
||||||
guard let last_word = post.components(separatedBy: .whitespacesAndNewlines).last else {
|
guard cursor > 0 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let last_word = post[...post.index(post.startIndex, offsetBy: cursor - 1)].components(separatedBy: .whitespacesAndNewlines).last else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ struct UserSearch: View {
|
|||||||
let search: String
|
let search: String
|
||||||
|
|
||||||
@Binding var post: NSMutableAttributedString
|
@Binding var post: NSMutableAttributedString
|
||||||
|
@Binding var cursor: Int
|
||||||
|
|
||||||
var users: [SearchedUser] {
|
var users: [SearchedUser] {
|
||||||
guard let contacts = damus_state.contacts.event else {
|
guard let contacts = damus_state.contacts.event else {
|
||||||
@@ -36,38 +37,57 @@ struct UserSearch: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all characters after the last '@'
|
// Remove all characters after the '@' and before the cursor
|
||||||
removeCharactersAfterLastAtSymbol()
|
let newCursor = removeCharactersAfterAtSymbol()
|
||||||
|
|
||||||
// Create and append the user tag
|
// Create and append the user tag
|
||||||
let tagAttributedString = createUserTag(for: user, with: pk)
|
let tagAttributedString = createUserTag(for: user, with: pk)
|
||||||
appendUserTag(tagAttributedString)
|
insertUserTag(tagAttributedString, cursor: newCursor)
|
||||||
|
|
||||||
|
cursor = newCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removeCharactersAfterLastAtSymbol() {
|
private func removeCharactersAfterAtSymbol() -> Int {
|
||||||
while post.string.last != "@" {
|
let newCursor = cursor
|
||||||
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
|
|
||||||
|
guard newCursor > 0 else {
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
|
|
||||||
|
var atSymbolOffset = newCursor
|
||||||
|
while atSymbolOffset > 0 && post.string[post.string.index(post.string.startIndex, offsetBy: atSymbolOffset - 1)] != "@" {
|
||||||
|
atSymbolOffset -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var endOfWordOffset = newCursor
|
||||||
|
while endOfWordOffset < post.string.count && !post.string[post.string.index(post.string.startIndex, offsetBy: endOfWordOffset)].isWhitespace {
|
||||||
|
endOfWordOffset += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post.deleteCharacters(in: NSRange(location: atSymbolOffset - 1, length: endOfWordOffset - atSymbolOffset + 1))
|
||||||
|
|
||||||
|
return atSymbolOffset - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createUserTag(for user: SearchedUser, with pk: String) -> NSMutableAttributedString {
|
private func createUserTag(for user: SearchedUser, with pk: String) -> NSMutableAttributedString {
|
||||||
let name = Profile.displayName(profile: user.profile, pubkey: pk).username
|
let name = Profile.displayName(profile: user.profile, pubkey: pk).username
|
||||||
let tagString = "@\(name)\u{200B} "
|
let tagString = "\u{200B}@\(name)\u{200B} "
|
||||||
|
|
||||||
let tagAttributedString = NSMutableAttributedString(string: tagString,
|
let tagAttributedString = NSMutableAttributedString(string: tagString,
|
||||||
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
|
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
|
||||||
NSAttributedString.Key.link: "@\(pk)"])
|
NSAttributedString.Key.link: "@\(pk)"])
|
||||||
|
tagAttributedString.removeAttribute(.link, range: NSRange(location: 0, length: 1))
|
||||||
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
||||||
|
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: 0, length: 1))
|
||||||
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
||||||
|
|
||||||
return tagAttributedString
|
return tagAttributedString
|
||||||
}
|
}
|
||||||
|
|
||||||
private func appendUserTag(_ tagAttributedString: NSMutableAttributedString) {
|
private func insertUserTag(_ tagAttributedString: NSMutableAttributedString, cursor: Int) {
|
||||||
let mutableString = NSMutableAttributedString()
|
let mutableString = NSMutableAttributedString()
|
||||||
mutableString.append(post)
|
mutableString.append(post)
|
||||||
mutableString.append(tagAttributedString)
|
mutableString.insert(tagAttributedString, at: cursor)
|
||||||
post = mutableString
|
post = mutableString
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +108,10 @@ struct UserSearch: View {
|
|||||||
struct UserSearch_Previews: PreviewProvider {
|
struct UserSearch_Previews: PreviewProvider {
|
||||||
static let search: String = "jb55"
|
static let search: String = "jb55"
|
||||||
@State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55")
|
@State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55")
|
||||||
|
@State static var cursor: Int = 0
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
UserSearch(damus_state: test_damus_state(), search: search, post: $post)
|
UserSearch(damus_state: test_damus_state(), search: search, post: $post, cursor: $cursor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct TextViewWrapper: UIViewRepresentable {
|
struct TextViewWrapper: UIViewRepresentable {
|
||||||
@Binding var attributedText: NSMutableAttributedString
|
@Binding var attributedText: NSMutableAttributedString
|
||||||
|
@Binding var cursor: Int
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UITextView {
|
func makeUIView(context: Context) -> UITextView {
|
||||||
let textView = UITextView()
|
let textView = UITextView()
|
||||||
@@ -31,18 +32,21 @@ struct TextViewWrapper: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
func makeCoordinator() -> Coordinator {
|
||||||
Coordinator(attributedText: $attributedText)
|
Coordinator(attributedText: $attributedText, cursor: $cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Coordinator: NSObject, UITextViewDelegate {
|
class Coordinator: NSObject, UITextViewDelegate {
|
||||||
@Binding var attributedText: NSMutableAttributedString
|
@Binding var attributedText: NSMutableAttributedString
|
||||||
|
@Binding var cursor: Int
|
||||||
|
|
||||||
init(attributedText: Binding<NSMutableAttributedString>) {
|
init(attributedText: Binding<NSMutableAttributedString>, cursor: Binding<Int>) {
|
||||||
_attributedText = attributedText
|
_attributedText = attributedText
|
||||||
|
_cursor = cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
func textViewDidChange(_ textView: UITextView) {
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
|
attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
|
||||||
|
cursor = textView.selectedRange.upperBound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user